C/C++ 基础笔记(十一)类的进阶 本篇核心知识构造函数、析构函数、拷贝构造、浅拷贝与深拷贝、断言、静态成员、单例模式一、构造函数Constructor概念构造函数是类的特殊成员函数创建对象时自动调用用于初始化对象成员。特性语法规则函数名与类名完全相同。无返回值连 void 都没有不能返回值。创建对象时自动调用无需手动调用一定会调用构造函数。当类中没有显式定义构造函数会有默认构造函数类名形参表{ //参数表可以自定义参数类型、个数可以重载通过实参确定调用的函数 函数体; }默认构造函数编译器自动生成无参数、空函数体。样式类名(){}自定义构造后有显式定义构造函数默认构造自动消失。构造函数重载支持多个构造参数个数 / 类型不同。包括无参构造、有参构造、拷贝构造。初始化顺序成员定义顺序决定初始化顺序与初始化列表顺序无关。代码示例#include iostream using namespace std; ​ class Person { char name[20]; int age; public: // 无参构造 Person() { strcpy(name, 未知); age 0; } // 有参构造 Person(const char* n, int a) { strcpy(name, n); age a; } }; ​ int main() { Person p1; // 调用无参构造 Person p2(XXX, 20); // 调用有参构造 // Person p3 {XXX, 18}; //这种写法只能用在所有数据成员都是公有的 return 0; }拓展初始化列表构造函数可通过初始化列表初始化成员效率更高尤其适合 const / 引用成员。Person(const char* n, int a) : age(a) { // age(a) 前面是成员括号内是参数如果参数名相同可以不用this区分 strcpy(name, n); }二、析构函数Destructor概念析构函数是类的特殊成员函数对象生命周期结束时自动调用用于释放资源、清理内存。特性语法规则函数名~ 类名~是函数名的一部分。无返回类型和返回值、形参表必须是空的不能重载。调用时机栈区对象离开作用域自动调用堆区对象(new创建)delete时调用。生命周期结束时自动调析构。(因果关系不是调用了析构就结束生命周期)默认析构如果没有显式析构则编译器自动生成空函数体。执行顺序先构造、后析构先创建后析构、后创建先析构。代码示例class Person { public: Person() { cout 构造 endl; } ~Person() { cout 析构 endl; } }; ​ int main() { Person p; // 栈区创建 Person *p1 new Person(); // 堆区创建 delete p1 // 堆区析构 return 0; // 栈区析构 }拓展析构与生命周期析构是生命周期结束的善后操作不是结束原因。手动调用析构不结束对象仅执行清理逻辑。三、拷贝构造函数Copy Constructor概念拷贝构造是特殊构造函数用已有对象初始化新对象。特殊在首个显式定义的参数是当前类的引用类型特性语法类名(const 类名 别名)必须是 const 引用参数保护实参。调用场景1用一个现有的对象初始化新对象 隐式2用一个现有的对象创建另一个对象 显式函数参数为当前类的对象值传递。函数返回值为当前类的对象拷贝值返回return后出函数有析构函数会直接析构流程构造-拷贝-析构-析构。默认拷贝编译器自动生成逐字节浅拷贝。代码示例class Person { int age; public: Person(int a) : age(a) {} // 拷贝构造参数是当前类的引用类型 Person(const Person ref) { // 必须用引用 给形参分配临时内容把实参拷贝给形参 // 也可以用初始化列表当有成员必须初始化时const int age)必须写初始化列表 :age(ref.age) age ref.age; cout 拷贝构造 endl; } }; ​ int main() { Person p1(20); Person p2 p1; // 调用隐式拷贝构造 Person p3(p1); // 调用显式拷贝构造 // 注意 Person p4(0); //构造 Person p5; //构造 p5 p4; // 这里是赋值函数不是拷贝构造 }相似概念值传递 vs 引用传递拷贝构造必须用引用否则会无限递归传值→拷贝→传值…。四、浅拷贝与深拷贝概念浅拷贝默认拷贝一一给成员值指针共享同一块内存。深拷贝手动实现指针重新申请内存、复制数据各自独立。特性浅拷贝问题多个对象共享同一块堆内存。析构时重复释放内存触发断言错误。深拷贝实现拷贝构造中为指针重新申请内存。逐元素复制原数据。代码示例class Array { public: int* data; int size; public: // 构造函数 Array(int size 0) { this-size size; data new int[s]; // 申请内存 } // 浅拷贝 一一对应给值 Array(const Array other) { this-size other.size; this-data other.data; // 指针不能直接拷贝指向会共用内存 } // 深拷贝 先申请新的内存 再把原内存中的数据拷贝到新内存 Array(const Array other) { this-size other.size; data new int[size]; // 新内存 // 拷贝方法一 memcpy(this-data, other.data, sizeof(int)*size); // 拷贝方法二 for(int i0; isize; i) data[i] other.data[i]; // 在新内存中循环赋值 } ~Array() { delete[] data; // 释放指针内存 data nullptr; } };拓展何时用深拷贝类包含指针 / 动态内存成员时必须手动深拷贝。五、断言assert概念断言是调试宏用于检查条件是否为真假则终止程序并报错。特性头文件assert.h。语法assert(条件)。作用调试阶段校验逻辑合法性如指针非空、下标合法。发布模式定义NDEBUG可关闭断言。代码示例#include assert.h int main() { int a 5; assert(a 0); // 条件真继续 assert(a 0); // 条件假终止程序报错 return 0; }拓展断言 vs if断言调试用发布可关闭用于内部逻辑校验。if正式逻辑不可关闭用于正常业务判断。六、静态成员static概念静态成员属于整个类所有对象共享生命周期为程序全程。特性1. 静态成员变量定义static 类型 变量名;类内声明。初始化必须在类外全局区初始化类型 类名::变量名 值;。访问类名::变量名或 对象访问。特点所有对象共享同一份数据。2. 静态成员函数定义static 返回值 函数名(参数);。访问类名::函数名 ()。特点无 this 指针访问不了普通数据成员只能访问静态成员。代码示例class Student { public: static int count; // 静态变量 static void show() { // 静态函数 cout 学生数 count endl; } }; // 类外初始化 int Student::count 0; ​ int main() { Student::count 10; Student::show(); return 0; }拓展静态成员用途统计对象个数、全局配置、工具函数。七、单例模式Singleton概念单例模式确保类只有一个实例全局共享常用于管理类、工具类。特性核心步骤私有构造限制构造的使用提供一个静态函数禁止外部创建对象。静态实例类内静态指针 / 对象。静态获取函数返回唯一实例。两种实现饿汉模式程序启动即创建实例。不管是否需要先创建并且初始化优点简单、线程安全、没有并发问题缺点程序启动就占内存如果一直不用就浪费懒汉模式首次调用时创建实例。等到需要使用的时候再创建优点节省内存、用到才创建缺点多线程不安全必须加锁C11 之后有最简单写法代码示例// 饿汉模式 class Singleton { private: static Singleton instance; // 声明静态成员变量属于整个类唯一的单例对象 Singleton() {} // 构造函数私有化禁止外部直接创建对象保证唯一 public: static Singleton getInstance() { // 公共静态接口给外部获取唯一实例的入口 return instance; // 返回已经创建好的唯一实例 } }; Singleton Singleton::instance; // 类外全局初始化程序启动就创建 // 懒汉模式 class LazySingleton { private: static LazySingleton* instance; // 静态指针保存唯一实例的地址。初始为 nullptr还没创建对象 LazySingleton() {} // 构造函数私有化 禁止外部 new 对象保证只能内部创建 public: static LazySingleton* getInstance() { // 获取唯一实例的静态接口 if(!instance) // 判断如果还没创建实例 instance new LazySingleton(); // 第一次调用才创建 return instance; } }; LazySingleton* LazySingleton::instance nullptr; // 类外初始化静态指针一开始 nullptr还没有创建对象拓展单例注意需私有或禁用拷贝构造和赋值防止拷贝生成新实例。