ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C++中类内自动生成的成员函数

2026/8/25 14:10:43 拓冰建站 浏览量
C++中类内自动生成的成员函数 文章目录类内自动生成的成员函数1 显式要求编译器自动生成默认函数2 三法则3 五法则4 零法则5 示例双重释放问题零法则方式解决五法则方式解决类内自动生成的成员函数1 显式要求编译器自动生成默认函数当已经有带参构造函数的时候编译器不会自动提供默认构造函数可以使用使用default要求编译器提供默认构造函数。class MyClass { public: MyClass() default; // 要求编译器提供默认构造函数 MyClass(int a); ~MyClass() default; // 要求编译器提供默认的析构函数 }; MyClass data(); // 可以使用无参构造函数已经要求编译器提供了默认情况下要是没有手动实现构造函数编译器会自动生成默认的构造函数析构函数同理。编译器对默认生成的函数遵循以下逻辑。2 三法则如果你手动定义了下面三者中的任意一个那么几乎应当手动定义另外两个析构函数~T()拷贝构造函数T(const T)拷贝赋值运算符T operator(const T)三法则对应c98时代只要没有自定义析构函数则编译器就会生成默认的析构函数。如果只自定义了析构函数编译器依然会自动生成默认的拷贝构造、拷贝赋值函数。自动生成的拷贝构造、拷贝赋值函数中执行的是浅拷贝。如果类中分配了指针析构中进行了释放那么拷贝之后可能导致双重释放问题。如果只自定义了拷贝构造函数、拷贝构造函数中的一个编译器不会自动生成另一个。3 五法则c11扩展了移动语义变成了五法则——如果你手动实现了五法则中的任意一个那么你几乎应当手动实现另外四个析构函数~T()拷贝构造函数T(const T)拷贝赋值函数T operator(const T)移动构造函数T(T)移动赋值函数T operator(T)一旦你需要自定义析构函数特殊处理了析构函数中的逻辑那么大概率拷贝构造、拷贝赋值、移动构造、移动赋值这些函数中的逻辑也需要进行特殊处理才能满足要求。如果不手动实现五法则中的任意一个编译器可以自动生成默认版本的五个成员函数使用default显式指定等同于不手动实现如果只自定义了析构函数为了兼容c98的代码考虑很多代码不支持移动语义所以不会自动生成移动构造、移动赋值函数如果需要支持移动语义则需要程序员手动处理。4 零法则尽量不要自己手写析构、拷贝、移动这5个特殊成员全部用编译器默认生成不需要我们手写类不去手动管理裸资源把资源交给 RAII 容器管理std::string、std::vector、std::unique_ptr。5 示例双重释放问题手动管理裸资源的例子极易导致双重释放问题class MyClass { public: MyClass() { data new char[256]; } ~MyClass() { delete[] data; } // 只写自定义的析构函数 // 不写自定义的拷贝构造编译器自动生成内部是浅拷贝 private: char * data; }; MyClass A; // A 中的 data 通过 new 创建了一个空间 MyClass B A; // B 中的 data 通过浅拷贝与 A 中 data 指向了同一块地址 // 退出作用域后A 的析构和 B 的析构会同时释放该地址空间导致双重 delete 崩溃这里裸指针 char* 只是一个普通指针不是 RAII不会自动释放内存。只要你手写析构释放就要额外考虑拷贝、移动极容易出现问题。零法则方式解决改成 RAII使用零法则的方式来解决该问题。#include vector class MyClass { public: MyClass() : data(256) {} // 析构、拷贝构造、拷贝赋值全部编译器默认生成全部正确什么都不用写。 // 析构vector自己释放内存 // 拷贝构造\拷贝赋值vector做深拷贝 // 移动构造\移动赋值vector高效移动不拷贝内存 private: std::vectorchar data; // RAII容器自己管理内存 };五法则方式解决使用五法则的方式来解决该问题一般针对需要自定义的较为复杂的逻辑。class MyClass { public: MyClass() { data new char[256]; } // 析构 ~MyClass() { delete[] data; } // 拷贝构造深拷贝 MyClass(const MyClass other) { data new char[256]; memcpy(data, other.data, 256); } // 拷贝赋值深拷贝 MyClass operator(const MyClass other) { if(this other) return *this; delete[] data; data new char[256]; memcpy(data, other.data, 256); return *this; } // c11 补齐移动成员五法则 // 移动构造窃取资源不用new MyClass(MyClass other) noexcept { data other.data; other.data nullptr; // 源对象置空析构时不会 delete } // 移动赋值 MyClass operator(MyClass other) noexcept { if(this other) return *this; delete[] data; // 释放自己旧资源 data other.data; other.data nullptr; return *this; } private: char * data; };