什么时候会用到拷贝构造函数?

本文详细介绍了拷贝构造函数的应用场景,包括对象初始化、函数参数传递及返回值时的使用,并探讨了深拷贝和浅拷贝的区别及其应用场景。

一、什么时候会用到拷贝构造函数? 

          拷贝构造函数 Person(const Person & per) 特点:具有单个形参(常用const修饰,是对本身类类型的引用,因为是要拷贝同类型(相同类)的对象)

1当用类的一个对象初始化该类的另一个对象时.例如:
C/C++ code
1
2
3
4
5
int  main()
{
    point A(1,2);
    point B(A); //用对象A初始化对象B,拷贝构造函数被调用.
}

2 如果函数的形参是类的对象,调用函数时,进行形参和实参结合时.
C/C++ code
1
2
3
4
5
6
7
8
void  f(point p)
{
}
main()
{
    point A(1,2);
    f(A); //函数的形参为类的对象时,当调用函数时,拷贝构造函数被调用.
}
3 如果函数的返回值是类的对象,函数执行完成返回调用者时. C/C++ code
1
2
3
4
5
6
7
8
9
10
point g()
{
    point A(1,2);
    return  A; //函数的返回值是类的对象,返回函数值时,调用拷贝构造函数.
}
void  main()
{  
    point B;
    B = g();
}

二、什么时候有必要用拷贝构造函数?(类有有指针成员、创建新对象时候做特定的工作)

上述3种情况,如果没有涉及到深拷贝问题,就没有必要自己来编写拷贝构造函数,编译器有默认的可以很完美的完成任务
还一种情况就是变态的拷贝:在拷内过程中进行缩放,放大等处理,不过没什么用

//关于深拷贝 以下是引用:traceback:http://www.netology.cn/blog/user1/bachelor/archives/2006/375.html


如果一个类中含有指针成员变量,则在利用一个已存在对象构造新的对象时,就会面临两种选择:深拷贝和浅拷贝。
1. 浅拷贝只是将对象间对应的指针成员变量进行简单的拷贝,即拷贝结束后新旧对象的指针指向相同的资源(指针的值是相同的);这种拷贝会导致对象的成员不可用(当一个对象 destory后,另一个对象的指针成员不能访问),如下例:
     class Person
     {
        public :
        //....
        char * home;//the person's home 
        void SetHome(char * str)
        {home = str;}
        ~Person()
         {
           //...
           delete [] home;
         }
     }
    //....
    char * place = new char [20];
    strcpy(place,"China");
    Person *A = new Person();
    A->SetHome(place);
    
    Person * B= Person(A);
    delete A;
   //....
   此时对象A 和对象B的成员home值相同,如果A对象 destroy,则对象B的成员home指向的地址变为不可用(对象A撤销时将home指向的资源释放了)。


2. 深拷贝是相对于浅拷贝而言的,为了避免上述情况的发生,将上例中的代码改造:对象拷贝时将指针指向的内容拷贝,代码如下:
   class Person
     {
        public :
        //....
        char * home;//the person's home 
        void SetHome(char * str)
        {home = str;}
        
        Person & Person(const Person & per) //自己定义的拷贝构造函数
        {
             //...
             if(* this == per)//copy itself
     return *this;
             home = new char[strlen(per.home) +1];//alloc new memory
    strcpy(home,per.home);
    return * this;
        } 

        ~Person()
         {
           //...
           delete [] home;
         }
     }
  深拷贝之后,新旧对象的home成员指向的内容的值相同,而其自身的值不同。这样就可避免出现其中之一
  destroy 之后,另一对象的home成员不可用。

深拷贝缺点:(指针成员指向的数据量很大时)

  但是,此种开辟新的内存区域然后赋值拷贝的方法在面临指针成员指向的数据量很大时出现效率低下的问题,因此又有下面的处理方法:为指针保留引用计数(reference counting)。
  当类每次拷贝这个指针时,就增加它的计数;对象destroy时检察指针的引用计数,如果为0就删除其指向的资源,否则计数减小。如果指针所指数据相对恒定是,指针引用计数尤为有效。而且在数据量庞大的时候,可以有较高的执行效率。

虽然引用计数的处理方法貌似不错,但是其亦存在其弊端:在多线程的条件下,其执行效率尤为低下。


<think> 我们正在讨论C++中的拷贝构造函数。根据用户的问题,他想要了解拷贝构造函数的定义以及使用场景。结合提供的引用内容,我们可以总结如下: ### 拷贝构造函数的定义 拷贝构造函数是C++类的一种特殊构造函数,用于使用一个已存在的对象来初始化一个新对象。其定义必须满足以下特征: 1. **函数名与类名相同**(因为是构造函数) 2. **参数必须是同类对象的引用**(通常是常量引用`const ClassName&`) 3. **无返回值类型** 定义格式: ```cpp ClassName(const ClassName& other); // 标准形式 ``` ### 拷贝构造函数的调用场景 根据引用内容,拷贝构造函数主要在以下场景被调用: 1. **显式使用对象初始化新对象** 当使用一个已存在对象初始化另一个新对象时: ```cpp AA obj1; AA obj2(obj1); // 调用拷贝构造函数[^3] AA obj3 = obj1; // 调用拷贝构造函数(注意这不是赋值)[^3] ``` 2. **函数参数传递** 当对象作为函数参数**按值传递**时: ```cpp void func(AA obj); // 按值传参 int main() { AA original; func(original); // 调用拷贝构造函数创建形参obj[^2] } ``` 3. **函数返回对象** 当函数按值返回对象时(可能触发拷贝构造,但编译器通常会优化): ```cpp AA createAA() { AA local; return local; // 可能调用拷贝构造创建临时对象[^2] } ``` 4. **容器操作** 当对象被放入标准容器(如`vector`)时: ```cpp std::vector<AA> vec; AA item; vec.push_back(item); // 调用拷贝构造函数复制对象到容器[^1] ``` ### 默认拷贝构造函数 若未显式定义拷贝构造函数: - 编译器会自动生成默认版本 - 默认行为:**浅拷贝**(逐成员字节复制)[^2] - **风险**:若类管理动态内存,浅拷贝会导致多个对象共享同一资源,引发双重释放错误 ### 需要自定义拷贝构造函数的场景 当类涉及资源管理(如动态内存、文件句柄等)时,必须自定义拷贝构造函数实现**深拷贝**: ```cpp class String { public: String(const char* str = "") { m_data = new char[strlen(str) + 1]; strcpy(m_data, str); } // 自定义深拷贝 String(const String& other) { m_data = new char[strlen(other.m_data) + 1]; // 重新分配内存 strcpy(m_data, other.m_data); // 复制内容 } ~String() { delete[] m_data; } private: char* m_data; }; ``` ### 完整示例 ```cpp #include <iostream> #include <cstring> class Data { public: Data(const char* s) { size = strlen(s); ptr = new char[size + 1]; strcpy(ptr, s); std::cout << "构造函数: " << ptr << "\n"; } // 自定义拷贝构造函数(深拷贝) Data(const Data& other) { size = other.size; ptr = new char[size + 1]; // 关键:独立分配内存 strcpy(ptr, other.ptr); std::cout << "拷贝构造: " << ptr << "\n"; } ~Data() { std::cout << "析构: " << ptr << "\n"; delete[] ptr; } private: char* ptr; size_t size; }; void test(Data d) { // 按值传参触发拷贝构造 // 函数结束时会析构形参d } int main() { Data orig("Original"); Data copy1 = orig; // 场景1:显式初始化 test(orig); // 场景2:函数传参 return 0; } ``` **输出**: ``` 构造函数: Original 拷贝构造: Original ← copy1初始化 拷贝构造: Original ← test函数传参 析构: Original ← test形参析构 析构: Original ← copy1析构 析构: Original ← orig析构 ``` ### 关键总结 | 特性 | 说明 | |--------------------|----------------------------------------------------------------------| | **定义要求** | 参数为`const ClassName&`的构造函数 | | **默认行为** | 浅拷贝(可能引发资源冲突) | | **必须自定义场景** | 类管理动态内存、文件句柄等资源时 | | **禁用拷贝构造** | 可通过`= delete`禁止(如单例模式) | > **最佳实践**: > - 优先使用`const T&`传递对象避免拷贝 > - 资源管理类必须实现深拷贝或禁用拷贝(遵循Rule of Three/Five) §§相关问题§§ 1. 深拷贝和浅拷贝的具体区别是什么?[^4] 2. 如何通过`= delete`禁止拷贝构造函数? 3. 移动构造函数与拷贝构造函数有何性能差异? 4. 为什么拷贝构造函数参数必须是引用?[^2] 5. C++11后的Rule of Five包含哪些特殊成员函数? </think> ### C++拷贝构造函数:定义与使用场景详解 #### 一、拷贝构造函数定义 拷贝构造函数是特殊的构造函数,用于**使用已有对象初始化新对象**。其核心特征: ```cpp class ClassName { public: // 拷贝构造函数定义 ClassName(const ClassName& other); // ✅ 必须为同类型对象的常量引用 }; ``` **关键特性**: 1. **参数要求**:必须是同类对象的**常量引用**(`const ClassName&`) - 原因:避免无限递归(值传递会触发新的拷贝构造)[^2][^4] 2. **默认行为**:若未显式定义,编译器生成**浅拷贝**版本(按字节复制)[^2] 3. **调用时机**:对象初始化时**自动调用**(非赋值操作) #### 二、拷贝构造函数调用场景 1. **显式对象初始化** 使用现有对象直接创建新对象: ```cpp AA obj1; AA obj2(obj1); // ✅ 调用拷贝构造 [^3] AA obj3 = obj1; // ✅ 初始化而非赋值,调用拷贝构造 [^3] ``` 2. **函数参数传递(值传递)** 对象作为函数参数按值传递时: ```cpp void process(AA obj); // 值传递参数 int main() { AA original; process(original); // ✅ 调用拷贝构造创建形参obj [^2] } ``` 3. **函数返回对象(值返回)** 函数返回对象时创建临时副本: ```cpp AA createAA() { AA local; return local; // ✅ 可能调用拷贝构造(编译器可能优化)[^2] } ``` 4. **容器操作** 对象被放入标准容器时: ```cpp std::vector<AA> vec; AA item; vec.push_back(item); // ✅ 调用拷贝构造复制到容器 [^1] ``` #### 三、必须自定义拷贝构造的场景 当类涉及**资源管理**时,需自定义深拷贝: ```cpp class String { public: // 自定义深拷贝 String(const String& other) { size = other.size; data = new char[size + 1]; // 重新分配内存 strcpy(data, other.data); // 复制内容 } private: char* data; int size; }; ``` **必须自定义的情况**: - 类包含**动态内存**(如`new`分配的指针) - 持有**文件句柄**、**网络连接**等资源 - 需要**非浅拷贝行为**(如引用计数) #### 四、深浅拷贝对比 | 类型 | 行为 | 风险 | |--------|--------------------------|--------------------| | 浅拷贝 | 逐字节复制(默认行为) | 多个对象共享资源 → 双重释放 | | 深拷贝 | 创建资源的新副本(需自定义) | 安全独立 | #### 五、完整示例 ```cpp #include <iostream> #include <cstring> class DataBlock { public: DataBlock(const char* d) { size = strlen(d); data = new char[size + 1]; strcpy(data, d); } // 自定义深拷贝构造 DataBlock(const DataBlock& other) : size(other.size) { data = new char[size + 1]; strcpy(data, other.data); std::cout << "深拷贝构造调用\n"; } ~DataBlock() { delete[] data; } private: char* data; size_t size; }; int main() { DataBlock original("Hello"); DataBlock copy(original); // 触发深拷贝构造 return 0; } ``` **输出**: ``` 深拷贝构造调用 ``` > **关键原则**: > 1. 资源管理类必须自定义拷贝构造(遵循**Rule of Three**) > 2. 简单数据类可依赖编译器生成的浅拷贝 > 3. 禁止拷贝时使用`= delete`(如单例模式)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值