1、转换构造函数
一个类中构造函数可以重载,允许多个存在。如果构造函数只有一个参数(或参数带有默认值)且非当前类对象时,可以将其他类型自动转换为当前类类型,这个过程称为隐式类型转换。
如下示例中的三个构造函数都可以发生隐式类型转换,在使用等号初始化或赋值时自动转换。
这样的可以发生隐式类型转换的构造函数称为:转换构造函数。
示例代码:
#include<iostream>
using namespace std;
class CTest
{
public:
CTest(int a){} //转换构造函数
/*
CTest(int a, int b = 0){} //转换构造函数
CTest(int a = 0,int b = 0){} //转换构造函数
explicit CTest(int a = 0, int b = 0){} //禁止发生隐式类型转换
CTest(int a,int b){} //不是转换构造函数
*/
};
int main()
{
CTest tst(1); //调用带参的构造
CTest tst1 = 1; //合法操作,发生隐式类型转换,将int类型转换为CTest类型
tst1 = 2; //合法操作,发生隐式类型转换,将int类型转换为CTest类型
}
注意:如果是多个参数且无默认值时,则不能自动隐式类型转换。如果想要避免隐式类型转换,需在构造函数前加上关键字:explicit。
2.拷贝构造函数
拷贝构造函数,C++编译器默认提供的特殊的构造函数,在空类中它与无参构造并存,拷贝构造函数是众多构造函数中的一种。
“默认拷贝构造函数”用于创建一个新对象作为现有对象的副本,其作用是将现有对象的成员变量复制到新对象中。
参数为当前类对象的引用。与默认的无参构造不同,其函数体代码一般不为空,操作为:参数中对象成员依次给this对象成员进行初始化。
注意:当我们手动重构拷贝构造函数时,编译器就不会提供默认的拷贝构造函数了,当然也不会存在默认的无参构造了。
#include<iostream>
using namespace std;
class CTest
{
public:
CTest() {}
CTest(const CTest& tst) {}
};
int main()
{
CTest tst1; //调用无参构造
CTest tst2(tst1); //调用拷贝构造 或者写成 CTest tst2=tst1;
}
存在的问题:
默认拷贝构造函数是一个浅拷贝,当类中存在指针成员且指向了一个new出来的具体空间,拷贝构造函数只是将两个指针存储的地址进行拷贝,并不会处理指针指向的空间。这样就导致了多个对象 里的指针指向了同一个空间,那么会导致以下问题:
1.当其中一个对象通过指针修改其指向空间的值,那么其他对象再使用就是修改之后的值了,这样的情况多数不是我们预期的。
2.如果是new出来的空间,那么会导致多个对象回收同一块内存空间,引起非法操作错误。
示例代码:
#include<iostream>
using namespace std;
class Test
{
private:
int* p;
public:
Test(int x)
{
this->p = new int(x);
cout << "对象被创建" << endl;
}
~Test()
{
if (p != NULL)
{
delete p;
p = NULL;
}
cout << "对象被释放" << endl;
}
int getX() { return *p; }
};
int main()
{
Test a(10);
Test b = a;//会调用默认的拷贝构造函数
return 0;
}
执行结果:
解决办法:
深拷贝,它并不是一个固定的写法,而是一个解决的办法:即在拷贝构造时,如果参数对象中的指针成员指向了一个内存空间,那么在重构拷贝构造时,需要为当前this对象中指针成员额外开辟新的内存空间,并初始化对应的值。
class CTest
{
public:
int* m_p;
CTest(const CTest& tst)
{
//深拷贝
if (tst.m_p)
m_p = new int(*tst.m_p);
else
m_p = nullptr;
}
};
在某些情况下,可以使用指针或引用避免对象的值传递,也避免了浅拷贝问题。
3.默认operator=
空类中编译器会默认提供一个operator=函数,参数和返回值为当前类对象的引用,一旦手动重构,编译期就不再提供默认的了。
当用一个类对象给类的另一个对象赋值时,会调用默认的operator=函数。
默认的operator=函数的函数体代码不为空,参数中对象成员依次给this对象成员赋值。
默认operator=函数也是浅拷贝,解决办法深拷贝:
class CTest
{
private:
int m_a;
int* m_p;
public:
CTest& operator=(const CTest& tst)
{
if (this != &tst)
{
this->m_a = tst.m_a;
if (tst.m_p)
{
if (this->m_p)
{
*this->m_p = *tst.m_p;
}
else
{
this->m_p = new int(*tst.m_p);
}
}
else
{
if (this->m_p)
{
delete this->m_p;
}
this->m_p = nullptr;
}
}
}
};
4.总结
C++构造函数调用规则:http://t.csdnimg.cn/H5jOr
类的默认成员函数(共六个)
1.默认无参数构造
2.默认的拷贝构造
3.默认的operator=(赋值操作符重载)
4.默认析构函数
5.默认的取地址操作符重载
6.const修饰的取地址操作符重载