c++拷贝构造函数和赋值函数

1,拷贝构造函数仍然是构造函数,赋值函数不是构造函数
故而例如一个string类。
string a;
string b = a;//由于b还没有被创建,故而调用的是拷贝构造函数。针对这种情况一般写为string b(a);
string c;
b = c;//由于b已经被创建,调用的是赋值函数。
被赋值的对象存不存在是判断调用的拷贝构造还是赋值函数的根本。
2,什么时候需要拷贝构造函数和赋值函数?
例如当一个对象中有动态分配的内存的时候,或者简单说来拥有一个成员指针ptr。如果不加拷贝构造函数,而直接调用默认的拷贝构造函数的话,这样执行的是对象的位拷贝, 将两个对象的比特流一模一样,于是两个对象分别有一个指针ptr,它们指向实际上是同一块内存区域。当调用析构函数释放ptr的内存的时候,会被释放两次,这显然是不合理的。
摘录effective c++的原话:
解决这类指针混乱问题的方案在于,只要类里有指针时,就要写自己版本的拷贝构造函数和赋值操作符函数。在这些函数里,你可以拷贝那些被指向的数据结构,从而使每个对象都有自己的拷贝;或者你可以采用某种引用计数机制(见条款 m29)去跟踪当前有多少个对象指向某个数据结构。引用计数的方法更复杂,而且它要求构造函数和析构函数内部做更多的工作,但在某些(虽然不是所有)程序里,它会大量节省内存并切实提高速度。
如果你真的很确信不需要拷贝构造和赋值的时候,可以只声明这些函数(声明为private成员)而不去定义(实现)它们。这就防止了会有人去调用它们,也防止了编译器去生成它们。这样在你试图进行赋值或者copy的时候,编译器会给你相应的错误信息,避免你由于无意而进行这些操作
3,构造函数
尽量使用成员初始化列表而不用构造函数赋值。对象的创建分两步,首先是数据成员初始化,然后调用构造函数体。这样如果类的对象成员的初始化在构造函数中的话,就会被初始化两次,一次调用默认构造函数,一次调用赋值函数。如果在成员初始化列表中,就会仅仅调用一次拷贝构造函数来初始化。特别是const和引用数据成员只能用初始化,不能被赋值。“插一句关于const引用的总结:const引用只是表明不能通过此引用间接的改变被引用的对象,对象能不能被更改,取决于对象本身,仅仅是通过引用不可以更改,const放前放后没有关系。"
摘录的effective c++的原话:
养成尽可能使用成员初始化列表的习惯,不但可以满足const和引用成员初始化的要求,还可以大大减少低效地初始化数据成员的机会。但有一种情况下,对类的数据成员用赋值比用初始化更合理。这就是当有大量的固定类型的数据成员要在每个构造函数里以相同的方式初始化的时候。
manydatambrs::manydatambrs()
: a(1), b(1), c(1), d(1), e(1), f(1), g(1), h(1), i(0),
j(0), k(0), l(0), m(0)
{ ... }
manydatambrs::manydatambrs(const manydatambrs& x)
: a(1), b(1), c(1), d(1), e(1), f(1), g(1), h(1), i(0),
j(0), k(0), l(0), m(0)
{ ... }
写一个单独的init函数,让两个函数都去调用情况似乎会更好一些。
void manydatambrs::init()
{
a = b = c = d = e = f = g = h = 1;
i = j = k = l = m = 0;
}
4,赋值运算
函数定义的写法:
string& string::operator=(const string& rhs)
{
if (this == &rhs) return *this;
...//如果是派生类,还需要调用基类的赋值函数base::operator=(rhs);
//释放自身动态分配的内存,将rhs的成员赋值给自己
return *this; // 返回左边的对象
}
5,拷贝构造函数
string::string(const string& rhs)
{
*this = rhs;//调用赋值函数
}
当定义自己的赋值运算符时,必须返回赋值运算符左边参数的引用,*this。如果不这样做,就会导致不能连续赋值,或导致调用时的隐式类型转换不能进行,或两种情况同时发生。
6,技巧总结:
类里有指针的时候,就要写自己版本的拷贝构造和赋值函数。
如果确认不会有拷贝和赋值的情况,那就声明这些函数为private类型,阻止编译器生成默认的。
尽量采用成员初始化列表,少使用构造函数赋值。针对const和引用数据成员,必须采用初始化列表。
赋值函数,需要判定是否自己对自己赋值,如果是派生类需要调用基类的赋值函数,如果自己已经alloc了内存,需要首先释放,然后赋值,最好返回*this.

拷贝构造函数可以通过调用赋值函数来实现构造操作。


拷贝构造函数:只有单个形参,并且该形参时本类对象的引用(经常使用const修饰)的构造函数.
默认拷贝构造函数:
在没有定义拷贝构造函数的时候,编译器将自动生成一个
默认拷贝构造函数将执行的操作:对该类的每个成员执行初始化(对于基础类型直接赋值,对于其所有对象成员逐个调用其对应的拷贝构造函数.)
特别的:倘若一个类拥有数组成员,默认拷贝构造函数将复制数组(即复制数组中的每一个元素).
自定义拷贝构造函数:
包含对象成员或基础类型成员的类(即不包含指针),无需显示定义拷贝构造函数.使用默认的即可.
包含指针成员的类,倘若必须自定义拷贝构造函数(或者定义未定义的私有拷贝构造函数以放置被外部拷贝). 倘若要在拷贝构造函数时需要做一些操作也应该自定义拷贝构造函数.
禁止调用拷贝构造函数:
将拷贝构造函数显示声明为私有.(如果连友元和成员函数也禁止执行拷贝操作,则声明拷贝构造为私有后不进行定义)

 

=重载:
类的默认赋值函数:
在没有重载赋值函数时,编译器将自动生成一个.
默认赋值函数将执行的操作:对该类的每个成员执行赋值操作(对于基础类型直接赋值,对于其所有对象成员逐个调用其对应的赋值函数.)倘若一个类拥有数组成员,默认赋值函数将对数组中的每一个元素执行赋值操作.
[自定义赋值函数]的条件和拷贝构造函数相同.
[禁止赋值函数]的方法同样和拷贝构造函数相同.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值