c++ 构造函数 拷贝构造函数 析构函数 友元函数 static 运算符重载 new和delete

本文深入探讨C++中的构造函数、析构函数、拷贝构造函数、友元及静态成员的概念与用法,解析new与delete的内存管理机制,以及运算符重载的规则与实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

构造函数

构造函数:与类名相同的特殊成员函数
  1. 在对象完成创建的时候就自动调用进行初始化
  2. 无返回值,与类名相同,默认是无参数的,可以重载,可默认参数
  3. 一旦有显示的构造函数,那么默认的构造函数就会自动的隐藏
  4. 当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。
  5. 初始化列表中的初始化顺序,与声明顺序有关,与前后赋值顺序无关。

析构函数

析构函数:在类中定义的一个特殊的成员函数,用来清理对象,命名方式(~类名)
  1. 对象销毁的时候自动的调用
  2. 无返值 ,与类名同。无参。不可以重载与默认参数
    析构函数的作用,并不是删除对象,而在对象销毁前完成的一些清理工作。
    先构造的后析构

拷贝构造函数

拷贝构造函数:由己存在的对象,创建新对象。也就是说新对象,不由构造函数来构造,而是由
拷贝构造函数来完成。拷贝构造函数的格式是固定的。
class 类名
{
	类名(const 类名 & another)
	{
	拷⻉贝构造体
	}
}
class	A
{
A(const	A & another){}
}
  1. 系统提供默认的拷贝构造器。一经实现,不复存在。
  2. 系统提供的时等位拷贝,也就是所谓的浅浅的拷贝。
  3. 要实现深拷贝,必须要自定义。

友元

友元位置:友元声明以关键字 friend 开始,它只能出现在类定义中。
因为友元不是授权类的成员,所以它不受其所在类的声明区域 public private 和 protected 的影响。
通常我们选择把所有友元声明组织在一起并放在类头之后.

友元利弊:友元不是类成员,但是它可以访问类中的私有成员。友元的作用在于提高
程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问
类的私有成员。不过,类的访问权限确实在某些应用场合显得有些呆板,从而容
忍了友元这一特别语法现象。

注意事项
1.友元关系不能被继承。
2.友元关系是单向的,不具有交换性。若类 B 是类 A 的友元,类 A 不一定是类
B 的友元,要看在类中是否有相应的声明。
3.友元关系不具有传递性。若类 B 是类 A 的友元,类 C 是 B 的友元,类 C 不一
定 是类 A 的友元,同样要看类中是否有相应的声明。

友元函数

友元函数:友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外
的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元
的名称前加上关键字 friend

一个函数可以是多个类的友元函数,只需要在各个类中分别声明。

友元对象

友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中
的隐藏信息(包括私有成员和保护成员)。
当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类
的友元类。

static

静态成员是属于整个类的而不是某个对象,静态成员变量只存储
一份供所有对象共用。

类的静态成员,属于类,也属于对象,但终归属于类。
注意事项:

  1. static 成员变量实现了同类对象间信息共享。
  2. static 成员类外存储,求类大小,并不包含在内。
  3. static 成员是命名空间属于类的全局变量,存储在 data 区。
  4. static 成员只能类外初始化。
  5. 可以通过类名访问(无对象生成时亦可),也可以通过对象访问。
  6. 静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员, 完成对静态数据成员的封装。
  7. 静态成员函数只能访问静态数据成员。原因:非静态成员函数,在调用时this 指针被当作参数传进。而静态成员函数属于类,而不属于对象,没有 this 指针。
  8. 静态成员函数、成员变量属于类
  9. 静态成员函数不包含指向具体对象的指针

运算符重载

operate后面加运算符 中间不能有空格 例如operate+

  1. C++不允许用户自己定义新的运算符,只能对已有的 C++运算符进行重载。
  2. 重载不能改变运算符运算对象(即操作数)的个数。
  3. 重载不能改变运算符的优先级别。
  4. 重载不能改变运算符的结合性。
  5. 重载运算符的函数不能有默认的参数
  6. 重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用)。
  7. 用于类对象的运算符一般必须重载,但有两个例外,运算符”=“和运算符”&“不 必用户重载。
  8. 应当使重载运算符的功能类似于该运算符作用于标准类型数据时候时所实现的功能。
  9. 运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数

new和delete

用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正
常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分
配空间是否成功。
malloc不会调用类的构造函数,而new会调用类的构造函数
Free不会调用类的析构函数,而delete会调用类的析构函数

<think>在C++中,我们可以使用`= delete`来显式禁止某些函数的自动生成或使用。对于拷贝构造函数赋值运算符重载,使用`= delete`的时机通常是为了防止对象的复制或赋值。以下是详细解释: ### 1. **禁止拷贝构造赋值运算符的时机** 当类的实例具有唯一性(例如,管理独占资源的类)或复制操作在逻辑上不合理时,需要禁止拷贝构造赋值操作。常见场景包括: - 资源管理类(如文件句柄、网络连接等) - 单例模式 - 包含不可复制成员(如`std::mutex`)的类 ### 2. **使用`= delete`的代码示例** ```cpp class NonCopyable { public: NonCopyable() = default; // 禁止拷贝构造 NonCopyable(const NonCopyable&) = delete; // 禁止赋值运算符 NonCopyable& operator=(const NonCopyable&) = delete; }; class FileHandler { private: FILE* file; public: explicit FileHandler(const char* filename) { file = fopen(filename, "r"); if (!file) throw std::runtime_error("File open failed"); } ~FileHandler() { if (file) fclose(file); } // 禁止拷贝(文件句柄不可共享) FileHandler(const FileHandler&) = delete; FileHandler& operator=(const FileHandler&) = delete; // 允许移动构造移动赋值(C++11起) FileHandler(FileHandler&& other) noexcept : file(other.file) { other.file = nullptr; } FileHandler& operator=(FileHandler&& other) noexcept { if (this != &other) { if (file) fclose(file); file = other.file; other.file = nullptr; } return *this; } }; ``` ### 3. **与旧方法的对比** 在C++11之前,通过声明私有拷贝构造函数赋值运算符(但不实现)来禁止复制: ```cpp class LegacyNonCopyable { private: LegacyNonCopyable(const LegacyNonCopyable&); // 只声明不实现 LegacyNonCopyable& operator=(const LegacyNonCopyable&); public: LegacyNonCopyable() = default; }; ``` 但这种方法: - 在友元或成员函数中仍可能错误调用 - 报错信息不如`= delete`明确 - 无法在类外捕获调用 ### 4. **关键注意事项** 1. **隐式生成规则**: - 如果声明了移动操作(移动构造/移动赋值),编译器会**自动删除**拷贝操作 - 如果声明了拷贝操作(拷贝构造/赋值),编译器会**自动删除**移动操作 2. **显式删除其他函数**: ```cpp // 禁止特定参数类型的调用 class OnlyInt { public: void foo(int) {} void foo(double) = delete; // 禁止double参数调用 }; ``` 3. **删除析构函数**: ```cpp class CannotDestroy { public: ~CannotDestroy() = delete; // 此类对象无法被销毁(只能通过new创建且不能delete) }; ``` ### 5. **何时使用`= default`** 当需要显式要求编译器生成默认实现时(通常在声明了其他构造函数时): ```cpp class DefaultOps { public: DefaultOps() = default; DefaultOps(int) {} // 显式要求生成默认拷贝操作 DefaultOps(const DefaultOps&) = default; DefaultOps& operator=(const DefaultOps&) = default; }; ``` §§相关问题§§: 1. 在C++中,使用`= delete`删除成员函数与将其声明为`private`但不实现有何区别? 2. 如果一个类声明了移动构造函数拷贝构造函数会被自动删除吗? 3. 如何禁止类的移动操作(移动构造移动赋值)? 4. 删除析构函数会导致什么后果?这种设计有什么实际用途?</think>### 禁止拷贝构造赋值运算符的时机 (`= delete`) 在C++中,使用 `= delete` 显式删除拷贝构造函数赋值运算符是防止对象被复制或赋值的有效方式。以下是关键使用时机实现方法: #### 1. **禁止对象复制的场景** 当类表示唯一资源或需要独占所有权时,应禁止拷贝操作: ```cpp class UniqueResource { private: int* resource; public: explicit UniqueResource(int value) : resource(new int(value)) {} ~UniqueResource() { delete resource; } // 禁止拷贝构造赋值 UniqueResource(const UniqueResource&) = delete; UniqueResource& operator=(const UniqueResource&) = delete; // 允许移动操作 UniqueResource(UniqueResource&& other) noexcept : resource(other.resource) { other.resource = nullptr; } UniqueResource& operator=(UniqueResource&& other) noexcept { if (this != &other) { delete resource; resource = other.resource; other.resource = nullptr; } return *this; } }; // 使用示例 int main() { UniqueResource r1(42); // UniqueResource r2 = r1; // 错误:拷贝构造被删除 // r1 = UniqueResource(10); // 错误:拷贝赋值被删除 UniqueResource r3 = std::move(r1); // 允许移动构造 } ``` #### 2. **禁用特定类型的赋值** 当需要禁止特定类型的赋值操作时: ```cpp class Logger { public: // 禁止从const char*赋值 Logger& operator=(const char*) = delete; // 允许从string赋值 Logger& operator=(const std::string& msg) { log(msg); return *this; } }; ``` #### 3. **单例模式实现** 单例对象禁止复制: ```cpp class Singleton { private: static Singleton* instance; Singleton() = default; public: Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton* getInstance() { if (!instance) instance = new Singleton(); return instance; } }; Singleton* Singleton::instance = nullptr; ``` #### 4. **包含不可复制成员的类** 当类成员不可复制时: ```cpp class MutexGuard { private: std::mutex mtx; public: MutexGuard() = default; // 禁止拷贝(因为mutex不可复制) MutexGuard(const MutexGuard&) = delete; MutexGuard& operator=(const MutexGuard&) = delete; }; ``` ### 关键使用时机总结 | 场景 | 拷贝构造 `= delete` | 赋值运算符 `= delete` | |------|---------------------|----------------------| | **独占资源管理** | ✓ | ✓ | | **单例模式** | ✓ | ✓ | | **移动专属类型** | ✓ | ✓ | | **包含不可复制成员** | ✓ | ✓ | | **禁用特定类型转换** | ✗ | ✓ | ### 与旧方法的对比(C++11之前) ```cpp // 传统禁用方法(C++98) class LegacyNonCopyable { private: LegacyNonCopyable(const LegacyNonCopyable&); // 只声明不实现 LegacyNonCopyable& operator=(const LegacyNonCopyable&); public: LegacyNonCopyable() = default; }; // 现代方法(C++11+) class ModernNonCopyable { public: ModernNonCopyable() = default; ModernNonCopyable(const ModernNonCopyable&) = delete; ModernNonCopyable& operator=(const ModernNonCopyable&) = delete; }; ``` **优势对比**: 1. 更清晰的意图表达 2. 更好的错误信息(明确显示"deleted function") 3. 可在public区域声明(更直观) 4. 可禁用特定重载版本 ### 特殊注意事项 1. **移动操作的影响**: ```cpp class AutoDeleteMove { public: // 声明移动操作会禁用拷贝操作 AutoDeleteMove(AutoDeleteMove&&) = default; AutoDeleteMove& operator=(AutoDeleteMove&&) = default; // 编译器自动生成: // AutoDeleteMove(const AutoDeleteMove&) = delete; // AutoDeleteMove& operator=(const AutoDeleteMove&) = delete; }; ``` 2. **析构函数的特殊规则**: ```cpp class CannotDestroy { public: ~CannotDestroy() = delete; // 禁止销毁 }; // 使用方式(只能通过new创建,不能delete) auto p = new CannotDestroy(); // delete p; // 错误:析构函数被删除 ``` 3. **模板特例禁用**: ```cpp template <typename T> class Box { public: // 禁用浮点类型的赋值 template <typename U = T> std::enable_if_t<std::is_floating_point_v<U>, Box&> operator=(const Box&) = delete; }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值