当前位置:   article > 正文

C++之拷贝构造

C++之拷贝构造

1、转换构造函数

一个类中构造函数可以重载,允许多个存在。如果构造函数只有一个参数(或参数带有默认值)且非当前类对象时,可以将其他类型自动转换为当前类类型,这个过程称为隐式类型转换。
如下示例中的三个构造函数都可以发生隐式类型转换,在使用等号初始化或赋值时自动转换。
这样的可以发生隐式类型转换的构造函数称为:转换构造函数

示例代码:

  1. #include<iostream>
  2. using namespace std;
  3. class CTest
  4. {
  5. public:
  6. CTest(int a){} //转换构造函数
  7. /*
  8. CTest(int a, int b = 0){} //转换构造函数
  9. CTest(int a = 0,int b = 0){} //转换构造函数
  10. explicit CTest(int a = 0, int b = 0){} //禁止发生隐式类型转换
  11. CTest(int a,int b){} //不是转换构造函数
  12. */
  13. };
  14. int main()
  15. {
  16. CTest tst(1); //调用带参的构造
  17. CTest tst1 = 1; //合法操作,发生隐式类型转换,将int类型转换为CTest类型
  18. tst1 = 2; //合法操作,发生隐式类型转换,将int类型转换为CTest类型
  19. }

 注意:如果是多个参数且无默认值时,则不能自动隐式类型转换。如果想要避免隐式类型转换,需在构造函数前加上关键字:explicit

2.拷贝构造函数
拷贝构造函数,C++编译器默认提供的特殊的构造函数,在空类中它与无参构造并存,拷贝构造函数是众多构造函数中的一种。
“默认拷贝构造函数”用于创建一个新对象作为现有对象的副本,其作用是将现有对象的成员变量复制到新对象中。
参数为当前类对象的引用。与默认的无参构造不同,其函数体代码一般不为空,操作为:参数中对象成员依次给this对象成员进行初始化。

注意:当我们手动重构拷贝构造函数时,编译器就不会提供默认的拷贝构造函数了,当然也不会存在默认的无参构造了。

  1. #include<iostream>
  2. using namespace std;
  3. class CTest
  4. {
  5. public:
  6. CTest() {}
  7. CTest(const CTest& tst) {}
  8. };
  9. int main()
  10. {
  11. CTest tst1; //调用无参构造
  12. CTest tst2(tst1); //调用拷贝构造 或者写成 CTest tst2=tst1;
  13. }

存在的问题:

默认拷贝构造函数是一个浅拷贝,当类中存在指针成员且指向了一个new出来的具体空间,拷贝构造函数只是将两个指针存储的地址进行拷贝,并不会处理指针指向的空间。这样就导致了多个对象 里的指针指向了同一个空间,那么会导致以下问题:

1.当其中一个对象通过指针修改其指向空间的值,那么其他对象再使用就是修改之后的值了,这样的情况多数不是我们预期的。
2.如果是new出来的空间,那么会导致多个对象回收同一块内存空间,引起非法操作错误。


示例代码:

  1. #include<iostream>
  2. using namespace std;
  3. class Test
  4. {
  5. private:
  6. int* p;
  7. public:
  8. Test(int x)
  9. {
  10. this->p = new int(x);
  11. cout << "对象被创建" << endl;
  12. }
  13. ~Test()
  14. {
  15. if (p != NULL)
  16. {
  17. delete p;
  18. p = NULL;
  19. }
  20. cout << "对象被释放" << endl;
  21. }
  22. int getX() { return *p; }
  23. };
  24. int main()
  25. {
  26. Test a(10);
  27. Test b = a;//会调用默认的拷贝构造函数
  28. return 0;
  29. }

 执行结果:


解决办法:

深拷贝,它并不是一个固定的写法,而是一个解决的办法:即在拷贝构造时,如果参数对象中的指针成员指向了一个内存空间,那么在重构拷贝构造时,需要为当前this对象中指针成员额外开辟新的内存空间,并初始化对应的值。
 

  1. class CTest
  2. {
  3. public:
  4. int* m_p;
  5. CTest(const CTest& tst)
  6. {
  7. //深拷贝
  8. if (tst.m_p)
  9. m_p = new int(*tst.m_p);
  10. else
  11. m_p = nullptr;
  12. }
  13. };

在某些情况下,可以使用指针或引用避免对象的值传递,也避免了浅拷贝问题。

3.默认operator=

空类中编译器会默认提供一个operator=函数,参数和返回值为当前类对象的引用,一旦手动重构,编译期就不再提供默认的了。

当用一个类对象给类的另一个对象赋值时,会调用默认的operator=函数。

默认的operator=函数的函数体代码不为空,参数中对象成员依次给this对象成员赋值。

默认operator=函数也是浅拷贝,解决办法深拷贝:
 

  1. class CTest
  2. {
  3. private:
  4. int m_a;
  5. int* m_p;
  6. public:
  7. CTest& operator=(const CTest& tst)
  8. {
  9. if (this != &tst)
  10. {
  11. this->m_a = tst.m_a;
  12. if (tst.m_p)
  13. {
  14. if (this->m_p)
  15. {
  16. *this->m_p = *tst.m_p;
  17. }
  18. else
  19. {
  20. this->m_p = new int(*tst.m_p);
  21. }
  22. }
  23. else
  24. {
  25. if (this->m_p)
  26. {
  27. delete this->m_p;
  28. }
  29. this->m_p = nullptr;
  30. }
  31. }
  32. }
  33. };

4.总结

C++构造函数调用规则:http://t.csdnimg.cn/H5jOr

类的默认成员函数(共六个)
1.默认无参数构造

2.默认的拷贝构造

3.默认的operator=(赋值操作符重载)

4.默认析构函数

5.默认的取地址操作符重载

6.const修饰的取地址操作符重载

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/737563
推荐阅读
相关标签
  

闽ICP备14008679号