C++笔记(下)

八、引用

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。eg:类似于给你取一个新的呼叫名,比如你叫李华,我可以叫你小华(当公司只有你一个带华字的),等于说我可以通过这个呼叫你(直接访问)。

 

引用相当于给这个内存中的数据提供了一个新的变量名。(我脑子存储了你叫小华)

 

引用很容易和指针混淆。

 

引用和指针的区别

1.不存在空引用。引用必须连接到一块合法的内存。

2.一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。

3.引用必须在创建时被初始化。指针可以在任何时间被初始化。

可以说变量名是变量在内存中的第一 个名字,对它的引用是其第二个名字;

int a=6; int* p=&a; *p=20;

可以为 a声明引用变量

int& r = a; double& s = d;

&读作引用。第一个声明读作” r 是一个初始化为 i 的整型引用“,第二个同理。

特性引用 (Reference)指针 (Pointer)
声明语法int& ref = var;int* ptr = &var
是否可为空不可为空,必须绑定有效对象 可为 nullptr
是否可重新绑定一旦绑定,不可更改目标可随时指向其他变量或 nullptr
访问目标值直接使用 ref需解引用 *ptr
内存地址存储 不存储地址,是变量的别名存储变量的地

 

 

 

 

 

 

 

 

 

#include <iostream> using namespace std; int main() { int value = 10; // 引用部分 int& ref = value; // ref 是 value 的别名 ref = 20; // 修改 ref 等同于修改 value cout << "引用修改后 value = " << value << endl; // 输出: 20 // 尝试重新绑定引用(不允许!编译报错) // int otherValue = 30; // ref = otherValue; // 这只是赋值,不是重新绑定!ref 仍是 value 的别名 // 指针部分 int* ptr = &value; // ptr 存储 value 的地址 *ptr = 30; // 修改指针指向的内容 cout << "指针修改后 value = " << value << endl; // 输出: 30 // 重新指向另一个变量(允许) int newValue = 40; ptr = &newValue; // ptr 现在指向 newValue *ptr = 50; // 修改 newValue cout << "newValue = " << newValue << endl; // 输出: 50 // 空指针( 允许) ptr = nullptr; // ptr 不指向任何对象 // cout << *ptr; // 不要这样做!会导致崩溃(解引用空指针) // 地址对比 cout << "\n 内存地址: " << endl; cout << "value 地址: " << &value << endl; cout << "ref 地址: " << &ref << endl; // 与 value 地址相同 cout << "ptr 地址: " << &ptr << endl; // 指针本身的地址 cout << "ptr 指向的地址: " << ptr << endl; // 当前 ptr是nullptr return 0; }

这是一个例子

#include<iostream> using namespace std; double val[]={10.1,12.6,33.1,24.1,50.0}; double& setValue(int i) { double& ref = val[i]; return ref; } int main() { setValue (3) = 90.9; cout<< vals[3] ; return 0; }

 

返回引用时,要注意被引用的对象不能超出作用域。所以返回一个局部变量的引用是不合法的。

 int& fun(){ int q; // return q; //编译发生错误 static int x; return x; //安全,在函数作用域外依然有效 }

 

为什么通过函数可以修改数组?

核心原理:引用传递

  • setValue 返回的是 val[i] 的引用(double&),而不是它的拷贝。
  • 对引用的修改会直接影响原始数组元素,因为引用是原始数据的“别名”。
函数类型示例代码修改是否生效
返回引用double& setValue(int i)修改原始数组
返回值double setValue(int i)只修改函数内副本

指针与引用情话:我希望我们之间是引用的关系,而非指针。(让我们像引用一样,成为彼此生命中不可分割的别名(alias),而不是可以随时指向他人的指针。" 在代码世界里,引用是最深情的绑定方式,正如人间最动人的爱情——不需要多余的语法糖,只需要一次确定的绑定,便是一生的承诺。)哥们玩抽象的。

技术层面:

引用(Reference):一旦初始化就无法改变指向,必须绑定到一个有效的对象,且生命周期与原对象一致。就像"我注定只能是你"。

指针(Pointer):可以随时改变指向,甚至可能悬空(dangling pointer)或指向null,象征关系的不确定性。

 

九、重载

函数重载

在同一个作用域内,可以声明几个功能类似的同名函数;

C++ 中,函数重载 是指 在同一作用域内,可以有多个同名函数,但它们的参数列表不同(参数的类型、个数或顺序不同)。编译器会根据调用时传入的实参来自动选择匹配的函数。

函数重载的规则:

  1. 函数名必须相同
  2. 参数列表必须不同(以下任一):
    • 参数个数不同
    • 参数类型不同
    • 参数顺序不同
  3. 返回类型可以不同,但不能仅靠返回类型不同来重载

 

实例代码:

1.参数个数不同

#include <iostream> using namespace std; // 计算两个数的和 int add(int a, int b) { cout << "调用两个参数的 add" << endl; return a + b; } // 计算三个数的和 int add(int a, int b, int c) { cout << "调用三个参数的 add" << endl; return a + b + c; } int main() { cout << add(2, 3) << endl; // 调用 add(int, int) cout << add(2, 3, 4) << endl; // 调用 add(int, int, int) return 0; }

2.参数类型不同

#include <iostream> using namespace std; // 整数相加 void printSum(int a, int b) { cout << "整数和: " << a + b << endl; } // 浮点数相加 void printSum(double a, double b) { cout << "浮点数和: " << a + b << endl; } int main() { printSum(3, 5); // 调用 int 版本 printSum(3.5, 6.7); // 调用 double 版本 return 0; }

3.参数顺序不同

#include <iostream> using namespace std; // 先 int 后 string void show(int a, string s) { cout << "int: " << a << ", string: " << s << endl; } // 先 string 后 int void show(string s, int a) { cout << "string: " << s << ", int: " << a << endl; } int main() { show(10, "Hello"); // 调用 show(int, string) show("World", 20); // 调用 show(string, int) return 0; }

4.结合默认参数(注意歧义)

#include <iostream> using namespace std; void greet(string name) { cout << "Hello, " << name << "!" << endl; } // 如果启用下面这行,会产生调用歧义! // void greet(string name = "Guest") { // cout << "Hi, " << name << "!" << endl; // } //程序会不清楚应该调用哪一个,下面的含有默认的参数,应该输出哪一个 int main() { greet("Alice"); // 如果有两个 greet,编译器不知道选哪个 return 0; }

5.重载构造函数(类中的函数重载)

#include <iostream> using namespace std; class Rectangle { private: int width, height; public: // 构造函数1:无参 Rectangle() { width = 1; height = 1; cout << "调用无参构造函数" << endl; } // 构造函数2:一个参数(正方形) Rectangle(int w) { width = w; height = w; cout << "调用单参构造函数" << endl; } // 构造函数3:两个参数 Rectangle(int w, int h) { width = w; height = h; cout << "调用双参构造函数" << endl; } void showArea() { cout << "面积: " << width * height << endl; } }; int main() { Rectangle r1; // 调用 Rectangle() Rectangle r2(5); // 调用 Rectangle(int) Rectangle r3(3, 4); // 调用 Rectangle(int, int) r1.showArea(); r2.showArea(); r3.showArea(); return 0; }

十、构造函数

类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

构造,构造的是什么:构造成员变量的初始值,内存空间等

构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回void。构造函数可用于为某些成员变量设置初始值。

以下是一个实例化

#include <iostream> #include <string> // 1. 定义一个名为 Book 的类 (class) class Book { private: // 私有成员变量,用于存储书的属性 std::string title; std::string author; int pages; bool is_borrowed; public: // 2. 默认构造函数 (Default Constructor) // 当没有提供参数时调用此构造函数 Book() { title = "未知标题"; author = "未知作者"; pages = 0; is_borrowed = false; std::cout << "调用了默认构造函数,创建了一本新书。\n"; } // 3. 带参数的构造函数 (Parameterized Constructor) // 当提供书名和作者时调用此构造函数 Book(const std::string& t, const std::string& a) { title = t; author = a; pages = 0; // 页数默认为0,可以在后续设置 is_borrowed = false; std::cout << "调用了带参数的构造函数,创建了《" << title << "》。\n"; } // 4. 带多个参数的构造函数 (Another Parameterized Constructor) // 当提供书名、作者和页数时调用此构造函数 Book(const std::string& t, const std::string& a, int p) { title = t; author = a; pages = p; is_borrowed = false; std::cout << "调用了带参数的构造函数,创建了《" << title << "》,共 " << pages << " 页。\n"; } // 5. 析构函数 (Destructor) // 对象销毁时自动调用 ~Book() { std::cout << "《" << title << "》这本书被销毁了。\n"; } // 6. 成员函数 (Member Functions) 用于操作对象的状态 void borrow() { if (!is_borrowed) { is_borrowed = true; std::cout << "《" << title << "》已被借出。\n"; } else { std::cout << "《" << title << "》已在借阅中。\n"; } } void returnBook() { if (is_borrowed) { is_borrowed = false; std::cout << "《" << title << "》已归还。\n"; } else { std::cout << "《" << title << "》未被借出。\n"; } } // Const成员函数,用于获取对象状态,不修改对象 void displayInfo() const { std::cout << "书名: " << title << "\n作者: " << author << "\n页数: " << pages << "\n状态: " << (is_borrowed ? "已借出" : "在馆") << "\n"; std::cout << "------------------------\n"; } }; int main() { std::cout << "--- C++ 构造函数与对象实例化演示 ---\n\n"; // 7. 实例化对象 (Instantiation) // 使用默认构造函数创建对象 book1 std::cout << "创建 book1 (使用默认构造函数):\n"; Book book1; // 不需要 'new',对象在栈上分配 book1.displayInfo(); // 使用带两个参数的构造函数创建对象 book2 std::cout << "\n创建 book2 (使用带参数的构造函数):\n"; Book book2("C++ Primer", "Stanley Lippman"); // 自动调用匹配的构造函数 book2.displayInfo(); // 使用带三个参数的构造函数创建对象 book3 std::cout << "\n创建 book3 (使用带参数的构造函数):\n"; Book book3("Effective C++", "Scott Meyers", 250); book3.displayInfo(); // 演示对象方法的使用 std::cout << "\n--- 演示对象方法 ---\n"; book2.borrow(); book2.displayInfo(); book3.borrow(); book3.borrow(); // 尝试再次借出 book3.displayInfo(); book2.returnBook(); book2.displayInfo(); std::cout << "\n--- 程序结束,局部对象将被自动销毁 ---\n"; // 当 main 函数结束时,book1, book2, book3 会自动调用析构函数并被销毁 return 0; } 

初始化列表

  • 初始化列表是构造函数的一部分,用于在构造函数体执行前初始化成员变量。
  • 必须使用初始化列表的场景:const 成员、引用成员、无默认构造函数的类成员。
  • 初始化顺序由类中声明的顺序决定,与初始化列表中的顺序无关

一个简单的示例

class MyClass{ private: int a; double b; std::string c; public: //使用初始化列表来初始化字段 Myclass(int x,double y,const std::string& z):a(x),b(y),c(z){ //构造函数体 } };
#include <iostream> using namespace std; // 定义一个没有默认构造函数的类 class Helper { public: Helper(int value) { cout << "Helper 构造函数: value = " << value << endl; } }; class MyClass { private: int normalVar; // 普通成员变量 const int constVar; // const 成员变量 int& refVar; // 引用成员变量 Helper helper; // 没有默认构造函数的类成员 public: // 构造函数使用初始化列表 MyClass(int a, int b, int c) : normalVar(a), // 初始化普通变量 constVar(b), // 初始化 const 变量 refVar(c), // 初始化引用变量 helper(c) // 初始化 Helper 对象 { cout << "MyClass 构造函数体" << endl; } void printValues() const { cout << "normalVar: " << normalVar << endl; cout << "constVar: " << constVar << endl; cout << "refVar: " << refVar << endl; } }; int main() { int value = 42; MyClass obj(10, 20, value); // 创建 MyClass 对象 obj.printValues(); // 打印成员变量的值 return 0; }

 

this关键字

在C++中,this关键字是一个指向调用对象的指针。它在成员函数内部使用,用于引用调用该函数的对象,使用this可以明确指出成员函数正在操作的是哪个对象操作的数据成员。

#include <iostream> #include <string> using namespace std; class Person { private: string name; int age; public: // 构造函数中使用 this 指针区分成员变量和参数 Person(string name, int age) { this->name = name; this->age = age; } // 设置姓名的 setter 方法 void setName(string name) { this->name = name; } // 设置年龄的 setter 方法 void setAge(int age) { this->age = age; } // 返回当前对象的引用,支持链式调用 Person& setInfo(string name, int age) { this->name = name; this->age = age; return *this; } // 打印信息的函数 void printInfo() const { cout << "Name: " << name << ", Age: " << age << endl; } }; int main() { // 创建对象并调用函数 Person p("Alice", 30); p.printInfo(); // 输出: Name: Alice, Age: 30 // 使用链式调用设置信息 p.setAge(31).setName("Bob").printInfo(); // 输出: Name: Bob, Age: 31 // 另一个示例:使用 setInfo 进行链式设置 Person p2("Charlie", 25); p2.setInfo("David", 26).printInfo(); // 输出: Name: David, Age: 26 return 0; }

注意这几点:

1.当构造函数的参数名与成员变量名相同时,必须使用 this-> 来明确指定是对对象成员的赋值。

2.链式调用(返回 *this):

return *this;

通过返回当前对象的引用,可以实现连续调用多个方法,如:

p.setAge(31).setName("Bob").printInfo();

3.printInfo 被声明为 const 方法: 表示该方法不会修改对象的状态,

 

用途示例
区分成员变量与参数this->name = name;
实现链式调用return *this;
传递当前对象someFunction(this);

new关键字

在C++中,new关键字用于动态分配内存。它是C++中处理动态内存分配的主要工具之一,允许程序运行时根据需要分配内存。

基本用法

分配单个对象:

使用new可以在堆上动态分配一个对象。例如,new int 会分配一个int类型的空间,并返回指向该空间的指针

int* p = new int; // 分配一个 int 类型的内存空间 int* q = new int(10); // 分配并初始化为 10

分配对象数组:

new 也可以用来分配一个对象数组。例如,new int[10]会分配一个包含10个整数的数组。

int* arr = new int[10];

分配类对象

MyClass* obj = new MyClass(); // 分配并调用构造函数

初始化:

可以在new表达式中使用初始化。用于单个对象,可以使用构造函数的参数。

MyClass* obj = new MyClass(arg1,arg2);

 

与delete配对使用

使用new分配的内存必须显式地通过delete(对于单个对象)或 delete[ ](对于数组)来释放,以避免内存泄露:

是释放单个对象

delete p;

 

释放数组

delete[ ] arr;

 

#include <iostream> #include <new> // 用于 std::nothrow class MyClass { public: MyClass() { std::cout << "MyClass created\n"; } ~MyClass() { std::cout << "MyClass destroyed\n"; } void print() { std::cout << "Hello from MyClass!\n"; } }; int main() { // 1. 分配单个对象 MyClass* obj1 = new MyClass(); obj1->print(); delete obj1; // 2. 分配数组 MyClass* objArray = new MyClass[3]; // 调用 3 次构造函数 delete[] objArray; // 调用 3 次析构函数 // 3. 安全分配(nothrow) int* largeArray = new (std::nothrow) int[1000000000]; if (largeArray) { std::cout << "Allocation succeeded!\n"; delete[] largeArray; } else { std::cerr << "Allocation failed!\n"; } // 4. 内存泄漏示例(注释掉 delete) int* leaky = new int(42); // delete leaky; // 内存泄漏 return 0; }

 

十一、析构函数

析构函数是C++中的一个特殊的成员函数,他在4对象生命周期结束时被自动调用,用于执行对象销毁前的清理工作。其十分重要,尤其是在涉及动态分配的资源(如内存、文件句柄、网络连接等)的情况下。

  1. 名称:析构函数的名称由波浪号(~)后跟类名组成,如 ~Myclass()。
  2. 无返回值和参数:析构函数不接受任何参数,也不返回任何值。
  3. 自动调用:当对象的生命周期结束时(例如,一个局部对象的作用域结束,或者使用delete删除一个动态分配的对象),析构函数会被自动调用。
  4. 不可重载:每一个类只能有一个析构函数。
  5. 继承和多态:如果一个类是多态基类,其析构函数应该是虚的。
class 类名 { public: ~类名(); // 声明析构函数 }; // 定义析构函数 类名::~类名() { // 清理逻辑 }

 

十二、静态成员

静态成员在C++类中是一个重要的概念,它包括静态成员变量和静态成员函数。

静态成员变量

  1. 定义:静态成员变量是类的所有对象共享的变量。与普通成员变量相比,无论创建了多少个类的实例,静态成员变量只有一份拷贝。
  2. 初始化:

Read more

2025年中秋月亮只有94.91%圆?Python告诉你真相

2025年中秋月亮只有94.91%圆?Python告诉你真相

前言: 又是一年中秋节,祝大家中秋快乐!作为程序员的我们,还有谁和我一样在外奔波而不能回家,想和大家说一声辛苦啦!既然不能回家吃月饼、赏明月,那我是不是也能用代码写下属于自己的中秋记忆,为朋友们送去我们自己特殊的中秋祝福,让技术和传统节日碰撞出新的火花。 本文目录: * 一、月相计算:今晚的月亮到底有多圆 * 1. 月相可视化 * 二、月饼切分算法:公平分配的艺术 * 1. 经典切分策略 * 2. 进阶问题:不过圆心的切分 * 三、诗词生成:中秋凑诗 * 四、月球数据可视化:用数据看月亮 * 1. 先画月球表面:模拟环形山地形 * 2. 再做月相动画:看一个月月亮怎么变 * 五、中秋快乐,记得吃月饼🥮 * 写在最后 一、月相计算:今晚的月亮到底有多圆 今天是中秋节,刷朋友圈的时候突然想到一个问题:今年中秋的月亮到底有多圆?作为Python开发者,我决定用代码来算一算。顺便整理了几个和中秋相关的有趣项目,

By Ne0inhk

PyBind11使用全解析,彻底搞懂C++与Python高效绑定

第一章:PyBind11使用全解析,彻底搞懂C++与Python高效绑定 PyBind11 是一个轻量级的头文件库,能够将 C++ 代码无缝暴露给 Python,实现高性能的跨语言调用。它利用现代 C++(C++11 及以上)特性,在编译期生成高效的绑定代码,相比传统的 SWIG 或 Boost.Python 更加简洁易用。 核心优势与基本结构 * 仅需包含头文件,无需额外链接库 * 支持 STL 容器、智能指针、类继承等复杂类型自动转换 * 编译后模块可直接通过 import 在 Python 中调用 快速入门示例 创建一个简单的 C++ 函数并绑定至 Python: #include <pybind11/pybind11.h> int add(

By Ne0inhk
【c++】AVL树(平衡搜索树)的概念讲解与模拟实现(万字详解)

【c++】AVL树(平衡搜索树)的概念讲解与模拟实现(万字详解)

小编个人主页详情<—请点击 小编个人gitee代码仓库<—请点击 c++系列专栏<—请点击 倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己! 目录 * 前言 * 一、AVL树的概念讲解 * 二、AVL树的模拟实现 * 铺垫 * 平衡因子 * 插入 * 四种旋转 * 左单旋 * 右单旋 * 右左双旋 * 左右双旋 * IsBalance * Height * 三、验证我们的代码是否可以构建出AVL树 * 简单验证 * 更严格更随机更大量的数据进行验证 * 四、源代码 * AVLTree.h * test.cpp * 总结 前言 【c++】c++异常——书接上文 详情请点击<—— 本文由小编为大家介绍——【c++】AVL树(

By Ne0inhk
C++短信通知接口示例代码:高性能C++发送通知短信的接口实现及调用逻辑

C++短信通知接口示例代码:高性能C++发送通知短信的接口实现及调用逻辑

在后端开发场景中,短信通知是系统触达用户的核心能力之一,而基于C++开发的高性能服务,对短信接口的调用效率、稳定性要求更高。本文将聚焦c++短信通知接口API示例代码,从底层调用逻辑拆解、实战代码实现、异常处理等维度,手把手教你实现高性能的C++短信通知接口调用,解决开发者在对接短信接口时遇到的参数配置、请求异常、性能优化等核心痛点。 一、C++短信通知接口调用核心原理拆解 1.1 短信接口调用的基本流程 C++调用短信通知接口本质是通过HTTP协议与短信服务商的服务端建立通信,完成参数传递与响应接收,核心流程可分为4步: 1. 构建符合接口规范的HTTP请求(包含请求头、核心参数); 2. 建立网络连接,发送请求到短信接口地址; 3. 接收服务端响应数据并解析; 4. 根据响应结果处理成功/失败逻辑,完成重试或日志记录。 1.2 高性能调用的关键设计要点 C++作为编译型语言,实现高性能短信接口调用需关注两个核心: * 网络请求层面:使用异步非阻塞IO(如libcurl的multi接口)避免主线程阻塞; * 数据处理层面:

By Ne0inhk