目录
一 概念
二 拷贝构造函数特性
1. 重载形式
2. 参数原则
3 默认拷贝函数
三 拷贝构造函数的实现
一 概念
在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)//默认构造函数
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
class Stack
{
public:
Stack(size_t capacity = 3)
{
cout << "Stack(size_t capacity = 3)" << endl;
_a = (int*)malloc(sizeof(int) * capacity);
if (nullptr == _a)
{
perror("malloc申请空间失败!!!");
}
_capacity = capacity;
_top = 0;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_a);
_capacity = _top = 0;
_a = nullptr;
}
private:
int* _a;
int _capacity;
int _top;
};
//值拷贝浅拷贝
void func1(Date d)//形参是实参拷贝
{
d.Print();
}
void func2(Stack st)//这样拷贝这是不行的
{
//...
}
int main()
{
Date d1(2023, 10, 22);
func1(d1);
Stack st1;
func2(st1);
return 0;
}
我们可以看到Date类就能传参 但是Stack类就不行, 这涉及到了浅拷贝, 深拷贝
如何解决?
规定, 自定义类型对象拷贝的时候, 调用一个函数, 这个函数就叫拷贝构造
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用
拷贝构造函数通常用于:
• 通过使用另一个同类型的对象来初始化新创建的对象。
• 复制对象把它作为参数传递给函数。
• 复制对象,并从函数返回这个对象。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)//默认构造函数
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void func1(Date d)//调用拷贝构造 -->形参是实参的拷贝
{
/*
(1).d1对象传入形参时,会先会产生一个临时变量,就叫 d。
(2).然后调用拷贝构造函数把d1的值给d。 整个这两个步骤有点像:Date d(d1);
(3).等func1()执行完后, 析构掉 d 对象。
*/
}
Date func2()
{
Date d2(2023, 11, 20);
return d2;//调用拷贝构造
/*
(1).先会产生一个临时变量,就叫XXXX吧。
(2).然后调用拷贝构造函数把d2的值给XXXX。整个这两个步骤有点像:Date XXXX(d2);
(3).在函数执行到最后先析构d2局部变量。
(4).等func2()执行完后再析构掉XXXX对象。
*/
}
int main()
{
Date d1(2023, 10, 22);
func1(d1);
Date d3(d1); //调用拷贝构造
Date d4 = d1;//调用拷贝构造
return 0;
}
二 拷贝构造函数特性
拷贝构造函数也是特殊的成员函数,其特征如下:
1. 重载形式
拷贝构造函数是构造函数的一个重载形式。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// Date(const Date d)--> 错误写法:编译报错,会引发无穷递归
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);//将d1拷贝给d2
return 0;
}
2. 参数原则
拷贝构造函数的参数只有一个且必须是类类型对象的引用
使用传值方式编译器直接报错, 因为会引发无穷递归调用。
3 默认拷贝函数
若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝
前面已经说了Date类就用编译器生成的默认拷贝构造函数就可以了 不用显示定义构造函数了
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请 时,则拷贝构造函数是一定要写的,需要深度拷贝,否则就是浅拷贝
三 拷贝构造函数的实现
像栈 这些涉及到了资源的申请, 那就需要深度拷贝了
class Stack
{
public:
Stack(size_t capacity = 3)
{
cout << "Stack(size_t capacity = 3)" << endl;
_a = (int*)malloc(sizeof(int) * capacity);
if (nullptr == _a)
{
perror("malloc申请空间失败!!!");
}
_capacity = capacity;
_top = 0;
}
// Stack st2(st1);
Stack(const Stack& stt)
{
cout << " Stack(Stack& stt)" << endl;
// 深拷贝
_a = (int*)malloc(sizeof(int) * stt._capacity);
if (_a == nullptr)
{
perror("malloc fail");
exit(-1);
}
memcpy(_a, stt._a, sizeof(int) * stt._top);
_top = stt._top;
_capacity = stt._capacity;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_a);
_capacity = _top = 0;
_a = nullptr;
}
private:
int* _a;
int _capacity;
int _top;
};
class MyQueue
{
Stack _pushst;
Stack _popst;
int _size = 0;
};
void func2(Stack st)
{
//...
}
int main()
{
Stack st1;
func2(st1);
Stack st2(st1);
MyQueue q1;
MyQueue q2(q1);
return 0;
}
总结:
Date 和 MyQueue 默认生成拷贝就可以用
1、内置类型成员完成值拷贝
2、自定义类型成员调用这个成员的拷贝构造
Stack需要自己写拷贝构造,完成深拷贝 顺序表、链表、二叉树等等的类,都需要深拷贝
本节难度比较简单, 但是对基础性要求较强, 对构造函数还有不太懂的朋友可以去我的博客:
【C++】类和对象(2)--构造函数-CSDN博客
继续加油!