【C++】拷贝构造函数之浅复制与深复制

el/2024/2/25 23:02:30

拷贝构造函数之浅复制与深复制


拷贝构造函数分为默认拷贝构造函数和自定义拷贝构造函数,默认拷贝构造函数是编译器自动为类对象创建的构造函数,而自定义拷贝构造函数则是根据自己需要创建的函数

特性

默认拷贝构造函数:简单地将参数对象的每个数据域复制给新建对象中的相应的副本。这个过程使用的是逐位复制(相当于赋值)的方式(最低成本的方式),是一种浅复制

自定义拷贝构造函数:通过在函数里创建新的对象来存放复制过来的数据,从而实现深复制

注意
当类定义里面有带有形参的一般构造函数或自定义的拷贝构造函数时,如果不显式地声明一个没有形参的默认构造函数,此时编译器不会自动创建没有形参默认构造函数,所以定义一个没有实参的对象是不允许的。

//编译器自动创建构造函数
#include<iostream>cladss foo() {
private:int m_a;
}int main() {foo b; //此时编译器会自动创建默认构造函数,可以这样定义一个对象
}//编译器不自动创建默认构造函数
class foo(){
public:foo(int a) {m_a = a;}foo(foo & b){m_a = b.m_a;}
private:int m_a;
}int main() {foo c; //error,此时编译器没有自动创建无形参的默认构造函数,所以没有函数能够定义这个对象foo d(1); //okfoo e(d); //ok
}

浅复制与深复制
当类里面没有指针对象时,使用c++自动创建的拷贝构造函数是没有问题的。但是,如果类里面有指针成员,使用浅复制就会出现错误,此时,我们应该自定义拷贝构造函数来实现深复制。

#include<iostream>class student{
public:student() {id = 0;name = new string("abc");//动态创建空间}student(student & stu) {name = stu.name;}~student() {delete name;//销毁对象前释放空间name = NULL;//使指针指向空,避免出现悬垂指针}string getname() {return *name;}
private:int id;string* name;//定义了一个指向string类型的指针
};
/*该程序会导致运行时错误。因为类里面的是默认的拷贝构造函数,是浅复制,所以,直接把对象a中指向name的指针给了b对象中的name,这时候,两个name所指的是同一片内存区域。当类对象使用完后会调用析构函数,删除之前开辟的空间,因为两个name都指向同一片区域,所以该区域会被释放两次,所以会导致运行时错误。*/
int main() {student a;student b(a);cout << a.getname() << endl;cout << b.getname() << endl;return 0;
}
//正确的做法是自己定义一个拷贝构造函数
#include<iostream>
#include<string>
using namespace std;class student{
public:student() {id = 0;name = new string("abc");}
//自己定义的拷贝构造函数,为新的对象先开辟一块新的空间,再将旧对象的内容复制过去。student(student & stu) {name = new string();*name = stu.getname();}~student() {delete name;}string getname() {return *name;}
private:int id;string* name;
};
//程序可以正确运行
int main() {student a;student b(a);cout << a.getname() << endl;cout << b.getname() << endl;return 0;
}

指向类的指针的深复制

#include<iostream>
using namespace std;class Date {
public:Date() {}Date(int y, int m, int d):year(y), month(m), day(d) {}int getYear() {return year;}
private:int year, month, day;
} 
class Person {
public:Person (int id, int year, int month, int day);Person (Person&);~Person();
private:int id;Date* birthday;
}
//正确实现
Person::Person (int id, int year, int month, int day) {this->id = id;birthday = new Date(year, month, day); //在申请一块存放Date类型数据的同时用参数初始化这个空间里的数据
}
Person::Person (Person& person) {this->id = person.id;birthday = new Date(person.birthday->getYear(), 1, 1); //同上,要在申请的时候初始化
}
//错误实现
Person::Person (Person& person) {this->id = person.id;birthday = new Date;birthday = person.birthday;/*这样做虽然为birthday申请了一块存放Date类型数据的空间,但是,后面的一个语句等于让birthday指针指向了person的birthday指针指向的内存空间,这样反而造成了内存泄露。所以这样并没有实现深复制。*/
}

小结
当类的数据成员里有指针的时候,需要自己定义拷贝构造函数,实现深复制。
当类的数据成员没有指针的时候,只需要利用c++提供的默认拷贝构造函数即可。
把指针深复制给指针的时候一定要注意指针重名和内存泄露的问题。


http://www.ngui.cc/el/4893678.html

相关文章

【C++】编程小结① -- new和delete

new 和 delete 使用的疏忽 1.谨记格式&#xff1a;指针所指对象数据类型* 变量名 new 数据类型&#xff08;初始化变量值,可省略&#xff09;&#xff1b; int* val new int;//val 指向一个没有初始化的int型对象&#xff08;int是内置数据类型&#xff0c;不会初始化&#…

【C++】编程小结②-- 字符串定义变量

关于字符串 如果要将字符一个个赋值进一个字符数组中并最后要用字符串的形式输出整个字符串&#xff0c;则需要手动在最后加入“\0”&#xff0c;不然编译器将读取不到结尾导致意想不到的后果。 定义变量的位置问题 c里允许在for循环里定义变量&#xff0c;但这是有局限性的。…

【C++】指向指针的指针int**的深复制的实现

实现指向指针的指针的深复制 如编写一个构造函数初始化一个矩阵。矩阵元素用一个二维数组来表示。 //定义了一个表示矩阵的类 class Matrix { public:Matrix(string name, int height, int width, int** element);~Matrix(); private:string name;int height;int width;int** …

【C++】编程小结③-- string 宏

关于string的“”号的使用 1.可以是两个string对象相加 #include<string> string s1 "abc"; string s2 "efg"; string s3 s1 s2; 2.和字符串字面值连接 当进行string对象和字符串字面值混合连接操作时&#xff0c;操作符的左右操作数必须至少…

【C++】Digest of 《effective C++》--1

《effective C》阅读笔记01 size_t是c计算个数时使用的某种不带正负号&#xff08;unsigned&#xff09;类型。它也是vector&#xff0c;deque和string内的operator[]函数接受的参数类型。分清楚声明式和定义式&#xff0c;对对象而言&#xff0c;定义式是编译器为此对象拨发内…

什么事情,都要去试一试啊

广州&#xff0c;下了一个月的雨&#xff0c;还在持续着超过一个月的阴天。 前段时间经历过一个心情的低谷。感觉自己真的变成了一个抑郁症患者&#xff0c;不想吃东西&#xff0c;不想做事情&#xff0c;对什么都提不起兴趣。那个时候的自己&#xff0c;可能受到各种外界的影…

【C++】标准IO库

本文章根据《C Primer》总结而来 IO类型在三个独立的头文件中定义&#xff1a; iostream定义读写控制窗口的类型fstream定义读写已命名文件的类型sstream定义读写存储在内存中的string对象 IO对象不可复制或赋值 1、只有支持复制的元素类型可以存储在vector或其他容器里&#…

【C++】关于const的使用

摘自《effective c》条款03 const可以修饰classes外部的global或namespace作用域中的常量、或文件、函数、或区块作用域&#xff08;block scope&#xff09;中被声明为static的对象。还可以修饰classes内部的static和non-static成员变量。 const对指针和迭代器的应用 面对指…

【JavaScript】类型、值和变量

JavaScript中非常重要的数据类型是对象和数组。JavaScript标识符必须以字母、下划线&#xff08;_&#xff09;或美元符&#xff08;$&#xff09;开始&#xff0c;后续字符可以是字母、数字、下划线或美元符。JavaScript变量是无类型的&#xff08;untyped&#xff09;&#x…

【JavaScript】表达式和运算符

表达式 原始表达式 原始表达式是表达式的最小单位&#xff0c;它们不再包含其他表达式。 数字直接量字符串直接量正则表达式直接量一些保留字变量 对象和数组的初始化表达式 对象和数组的初始化表达式实际上是一个新创建的对象和数组。 数组初始化表达式是通过一对方括号…