C++—拷贝构造函数

发布于:2025-07-11 ⋅ 阅读:(28) ⋅ 点赞:(0)

一、拷贝构造函数

1.定义

是特殊的构造函数,用一个已存在的对象来初始化另一个新对象。只拷贝数据成员,函数成员是共有的。

class ClassName {
public:
    // 拷贝构造函数
    ClassName(const ClassName& other) {
        // 成员变量的拷贝操作
    }
};

注意点:

(1)参数类型为const 类名&

①const的使用是为了防止修改被拷贝的对象

②&(引用)的使用是防止无限递归调用,如果把一个真实的类对象作为参数传递到拷贝构造函数,会无穷递归。

(2)当没有自定义拷贝构造函数时,编译器会自动生成一个默认的版本。默认拷贝构造函数会采用浅拷贝的方式,即逐个复制对象的成员变量。如果需要管理动态资源,需要手动处理深拷贝。

2.构造函数和拷贝构造函数的区别

①构造函数的参数列表可以是任意参数,而拷贝构造函数的参数是同类对象的引用

②构造函数在创建对象时调用,拷贝构造函数在用已有对象初始化新对象时调用

3.拷贝构造函数的使用场景

(1)用于拿一个对象初始化另一个对象

(2)函数的形参是类的对象

当对象作为值参数传递给函数时,会创建实参的副本,此时调用拷贝构造函数。

(3)函数的返回值是类对象

当函数返回对象时,会创建返回值的临时副本(亡值对象),此时调用拷贝构造函数。

亡值对象只存在函数调用中,函数结束亡值销毁。

注意:

①如果函数结束,但对象生存周期还未到,则可以使用引用或者指针返回。

②引用返回的场景:函数结束但变量仍然可以存在。

4.拷贝构造函数和赋值语句的重载

(1)拷贝构造函数的重载

用于创建一个新的对象,并用传入对象的值进行初始化。

(2)赋值语句的重载

用于将一个对象的值赋给另一个对象。

(3)赋值语句的重载与拷贝构造函数的区别

赋值语句重载需要处理自赋值的情况,并返回对象的引用以支持链式赋值

二、对象的生存期

1.动态创建对象

使用new操作符在堆上创建对象,并使用delete操作符释放对   

注意:new 和 delete 必须配对。

①使用 new 分配的内存必须用 delete 释放。

②使用 new[] 分配的内存必须用 delete[] 释放。

2.局部对象

局部对象的生存期由函数调用控制。从定义处开始,到作用域结束时自动销毁。

3.全局对象的生存期

全局对象的生存期由程序运行控制。程序启动时创建,程序结束时销毁。

三、内存泄漏和delete操作符的使用

1.内存泄漏

程序在动态分配内存后未正确释放,导致系统内存逐渐减少。

2.delete操作符的使用

用于释放动态分配的内存空间。

3.delete操作符的工作原理

调用对象的析构函数,并调用free释放堆区内存空间。

4.注意事项

delete操作符后应将指针置为空,避免悬挂指针。

四、内存分配的协议

1.new操作符的协议

当使用 new[] 分配一个包含析构函数的对象数组时,C++ 运行时通常会在实际对象数据前多分配 4 或 8 字节(取决于平台),用于存储以下信息:

(1)数组长度:记录数组中对象的数量,以便 delete[] 知道需要调用多少次析构函数。

(2)额外元数据:可能包括内存块大小、分配标志等。

注意:

仅对需要析构的对象有效,如果类没有自定义析构函数(且成员也不需要析构),编译器可能优化掉头部。

2.delete操作符的协议

当调用 delete[] 时,运行时会

(1)读取头部数据:获取数组长度。

(2)调用析构函数:按顺序为每个对象调用析构函数(次数 = 头部记录的长度)。

(3)释放内存:使用free将整个内存块(包括头部)归还给系统。

如图所示:(在visualStudio平台下,多分配4个字节)

3.new和malloc的区别

特性 new/delete malloc/free
构造 / 析构函数 自动调用构造函数和析构函数 不调用任何构造 / 析构函数
内存分配协议 可能多分配头部存储元数据(如数组长度) 仅分配请求的大小,无额外头部
类型安全 返回正确的指针类型 返回 void*,需要手动转换类型
异常处理 分配失败时抛出 std::bad_alloc 分配失败时返回 NULL
适用场景 面向对象编程(创建对象) 面向过程编程(分配原始内存)

总结:

new[] 的协议:为需要析构的对象数组多分配头部,存储数组长度。

delete[] 的协议:读取头部长度,调用对应次数的析构函数,释放整块内存。

与 malloc 的本质区别new 是面向对象的内存分配,自动管理对象生命周期;

              malloc 是低级内存分配,只处理原始字节。

五、常量对象和非常量对象

1.const与类型结合

表示该类型的变量是一个常变量

如图所示:

2.常量对象的定义

使用const关键字修饰对象,初始化后不可修改其状态,但可以被引用。
注意:必须在定义时初始化,且之后不能被赋值。

3.区别

特性 常量对象 非常量对象
状态修改 禁止修改 允许修改
可调用函数 仅常量成员函数 所有成员函数
mutable 成员 可修改 可修改
典型场景 只读数据、多线程共享对象 需动态修改的对象

ps:在C++中,合理的使用const可以提高代码的安全性和可读性!!!


网站公告

今日签到

点亮在社区的每一天
去签到