记录3-C++拷贝构造函数的意义

本文详细解析了拷贝构造函数的三大应用场景,包括函数返回类型为类对象、函数参数为类对象以及对象初始化。深入探讨了拷贝构造函数实现深拷贝的重要性,以及为何需要自定义拷贝构造函数避免指针悬挂现象。同时,对比了C++和C#在拷贝构造函数实现上的差异。

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

拷贝构造函数的目的就是完成对一个对象的拷贝,一般在一下三种场景下会调用该对象的拷贝构造函数:

1.函数返回类型是类的对象时

2.函数的参数是类的对象,调用该函数时会调用该类的拷贝构造函数

3.使用一个对象去初始化类的另一个对象时 F a = new F(1,2); F b = a; / F b(a) 注意必须要在类的初始化中使用,不是类的赋值

拷贝构造函数的关键意义在与可以实现深拷贝,即实现申请新的内存空间并复制其值。默认拷贝构造函数实现的是浅拷贝,即完成对象的一一复制但该对象还是指向堆中同一个对象。

为什么要实现自定义的拷贝构造函数?

当构造函数中实现了指针类型,则当多次调用默认的拷贝构造函数后,实际上多个对象都是指向堆中同一个内存,那么当调用析构函数时只有一次真正实现了内存释放,出现了指针悬挂现象。

为什么自定义拷贝构造函数必须是引用传递(&)而不能是值传递?

因为防止无限递归,因为如果是值传递则会再一次调用拷贝构造函数……所以自定义拷贝构造函数必须满足一下要求:

   解答:对于一个类X, 如果一个构造函数的第一个参数是下列之一:
   a) X&
   b) const X&
   c) volatile X&
   d) const volatile X&
   且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.

顺便提一下C#的拷贝构造函数:

实现方式类似,但是更加简单,这里的previousPerson居然不是引用类型,由此可以看出C#的类对象,在作为函数参数时不会调用拷贝构造函数的……

### C++ 中浅拷贝及其引发的问题 #### 浅拷贝的概念与问题 在C++中,当对象之间进行赋值操作或通过复制构造函数创建新对象时,默认情况下会执行浅拷贝。这意味着两个不同的对象将共享同一块动态分配的数据区域[^1]。 如果这两个对象都尝试去释放这块共同拥有的内存,则会发生双重删除错误,进而导致未定义行为,通常表现为程序崩溃或者更隐蔽的形式——即所谓的“内存泄漏”。具体来说: - 当第一个对象销毁并调用了析构函数来释放其指向的堆内寸时; - 第二个仍然持有相同地址的对象再次试图释放这段已经被释放过的内存时就会出现问题; 这种现象不仅限于显式的delete语句,在某些场景下还可能是由于异常抛出等原因造成的意外终止而导致部分清理工作未能完成。 ```cpp class String { private: char* data; public: // 默认复制构造函数只会做位级副本而非实际内容复制 }; ``` #### 引用计数机制简介 为了克服上述由浅拷贝带来的隐患之一(重复释放),可以采用引用计数技术作为替代方案。该方法的核心思想是在每一个实例内部维护一个额外变量用于记录当前有多少个外部实体正在使用它所持有的资源。每当有新的使用者加入时增加此数值;相反地减少直至为零则意味着没有任何地方再依赖于此份资料了,此时才允许真正意义上的回收动作发生[^2]。 ```cpp #include <iostream> using namespace std; class RefCountedString { private: class Data { friend class RefCountedString; int refcount; // 引用次数 string str; // 实际字符串数据 public: explicit Data(const string& s):refcount(1),str(s){} void addRef(){ ++refcount;} bool release(){ --refcount; return (0==refcount); } }; Data *pData=nullptr; public: // 构造函数初始化成员变量 pData 并设置初始引用计数为 1 explicit RefCountedString(const string &s){ pData=new Data(s); } // 复制构造函数实现引用计数加一而不重新分配内存 RefCountedString(const RefCountedString &other){ other.pData->addRef(); pData=other.pData; } ~RefCountedString(){ if(pData && pData->release()){ delete pData; } } }; int main() { cout << "Hello, World!" << endl; } ``` #### 内存泄漏的影响及预防措施 内存泄漏是指应用程序请求了一定量的计算机随机访问存储器(RAM),但在不再需要这部分空间之后却没有将其返回给操作系统或其他组件的情况。随着时间推移,这会导致可用物理内存逐渐耗尽,最终使得整个系统的性能受到影响甚至完全失效[^4]。 为了避免这种情况的发生,除了合理设计类之间的关系外,还可以采取如下策略: - 使用智能指针代替原始裸指针,如`std::shared_ptr`, `std::unique_ptr`; - 定期审查代码逻辑确保所有动态开辟出来的缓冲区都能得到妥善处理; - 对象生命周期结束前务必确认已正确解除对外部资源的所有权绑定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值