C++构造函数注意事项
1、匿名对象
首先应该明确匿名对象,匿名对象是之没有对象名,调用完构造函数后即析构的对象。下面通过代码捕捉类的构造函数和析构函数,以进行说明:
#include <iostream> using namespace std; class Solution{ public: Solution(int a, int b):m_num1(a), m_num2(b) { cout << "有参构造函数的调用" << endl; }; Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){ cout << "拷贝构造函数的调用" << endl; } ~Solution(){ cout << "析构函数的调用" << endl; } private: int m_num1; int m_num2; }; int main() { Solution(8,9); // Solution(8,9) 匿名对象 system("pause"); return 0; }
代码运行结果为:
通过代码运行结果可以看到,创建匿名对象的时候,调用了类的构造函数,随后立即调用了析构函数。我们可以直接利用匿名对象进行初始化类的成员的初始化,代码如下:
#include <iostream> using namespace std; class Solution{ public: Solution(int a, int b):m_num1(a), m_num2(b) { cout << "有参构造函数的调用" << endl; }; Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){ cout << "拷贝构造函数的调用" << endl; } ~Solution(){ cout << "析构函数的调用" << endl; } int m_num1; int m_num2; }; int main() { Solution s1(Solution(8,9)); // Solution s1 = Solution(8,9); //显式 Solution(8,9) 匿名对象初始化类成员 // Solution s1 = {8,9}; //{8,9}等价于Solution(8,9) cout << "s1.m_num1 = " << s1.m_num1 << endl; cout << "s1.m_num2 = " << s1.m_num2 << endl; system("pause"); return 0; }
运行结果如下:
代码调用了一次构造函数,可见匿名对象可以初始化类成员,就是将匿名对象转化成了s1对象,这里需要与拷贝构造函数区分开:
#include <iostream> using namespace std; class Solution{ public: Solution(int a, int b):m_num1(a), m_num2(b) { cout << "有参构造函数的调用" << endl; }; Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){ cout << "拷贝构造函数的调用" << endl; } ~Solution(){ cout << "析构函数的调用" << endl; } int m_num1; int m_num2; }; int main() { Solution s1(10,11); Solution s2(s1); cout << "s2.m_num1 = " << s2.m_num1 << endl; cout << "s2.m_num2 = " << s2.m_num2 << endl; system("pause"); return 0; }
代码运行结果为:
对比以上两个代码可以发现,通过匿名对象初始化类成员并不是拷贝构造,只是一种替换,通过匿名对象初始化类成员并不会调用拷贝构造函数。
2、拷贝构造函数的调用时机
今年秋招笔试题最爱考查构造函数的调用时机,通常会结合继承和多态来考察,这里先说明一下拷贝构造函数的调用时机,后面再详细说明带继承和多态的构造函数调用时机。
一、使用一个已经创建的对象来初始化一个新对象,如上面的代码,创建对象s1的时候调用了有参构造函数,通过s1来初始化s2的时候调用了拷贝构造。
二、函数的参数是需要值传递的对象的时候
三、函数返回对象的时候
下面通过代码验证,当函数的参数是一个需要值传递的对象的情况:
#include <iostream> using namespace std; class Solution{ public: Solution(int a, int b):m_num1(a), m_num2(b) { cout << "有参构造函数的调用" << endl; }; Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){ cout << "拷贝构造函数的调用" << endl; } ~Solution(){ cout << "析构函数的调用" << endl; } public: int m_num1; int m_num2; }; void showClassNum(Solution s) { cout << s.m_num1 << endl; cout << s.m_num2 << endl; } int main() { Solution s1(10,11); showClassNum(s1); system("pause"); return 0; }
代码运行结果为:
通过代码运行结果可以看到,s1对象调用了有参构造函数,当把s1传给函数的参数s的时候调用了拷贝构造函数,函数执行完调用了s对象的析构函数。
当函数的返回值是一个对象的时候,也会调用拷贝构造函数:
#include <iostream> using namespace std; class Solution{ public: Solution(int a, int b):m_num1(a), m_num2(b) { cout << "有参构造函数的调用" << endl; }; Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){ cout << "拷贝构造函数的调用" << endl; } ~Solution(){ cout << "析构函数的调用" << endl; } void changeNum(int a, int b) { m_num1 = a; m_num2 = b; } void showNum() { cout << "m_num1 = " << m_num1 << endl; cout << "m_num2 = " << m_num2 << endl; } public: int m_num1; int m_num2; }; Solution clearClassNum(Solution s) { s.changeNum(0,0); return s; } int main() { Solution s1 (10,10); s1 = clearClassNum (s1); s1.showNum(); system("pause"); return 0; }
代码运行结果为:
从代码运行结果可以看出来,函数传参的时候调用了拷贝构造,然后函数返回一个对象的时候的也调用了拷贝构造。
需要注意的是,函数要返回对象的时候,不要使用引用的方式返回,因为函数的内的变量存放在堆栈区,在函数执行完毕后就会释放这块内存,引用就会出现问题。
3、深拷贝和浅拷贝
面试的时候比较喜欢问的问题,首先浅拷贝就是我们常用的拷贝,实现了对象成员的拷贝,深拷贝就是在堆区申请空间,然后再进行拷贝操作,浅拷贝可以由编译器完成,但是深拷贝需要我们自己完成,就是有在堆区开辟的内存,就一定要自己提供拷贝构造函数,防止浅拷贝带来的重复内存释放问题。代码验证如下:
#include <iostream> using namespace std; class Solution{ public: Solution(int a, int b):m_num1(a), pm_num2(new int(b)) { cout << "有参构造函数的调用" << endl; }; //如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题,导致程序出错 Solution(const Solution& s):m_num1(s.m_num1), pm_num2(new int(*s.pm_num2)) { cout << "拷贝构造函数的调用" << endl; } ~Solution(){ cout << "析构函数的调用,释放堆区申请的内存" << endl; if (pm_num2 != nullptr) { delete pm_num2; } } void changeNum(int a, int b) { m_num1 = a; *pm_num2 = b; } void showNum() { cout << "m_num1 = " << m_num1 << endl; cout << "m_num2 = " << *pm_num2 << endl; } public: int m_num1; int* pm_num2; }; void func() { Solution s1 (10,10); Solution s2(s1); s2.showNum(); } int main() { func(); system("pause"); return 0; }
代码运行结果为:
- C++ 构造函数中使用new时注意事项
- C++中的构造函数使用方法和注意事项
- C++ 构造函数使用 ":成员变量(形参)" 的形式给类里面成员变量赋值,如果成员变量和形参是指针,那么需要注意的事项
- C++构造函数和析构函数中抛出异常的注意事项
- c++的构造函数与析构函数区别和使用析构函数的注意事项g
- c++中的构造函数注意事项
- c++ 构造函数注意事项
- C++:在构造函数中使用new时应该注意的事项
- C++ 与“类”有关的注意事项总结(七):构造函数
- [c++ primer plus]c++ compiler缺省生成了哪些构造函数?关于构造函数的注意事项。
- 探讨:C++中函数返回引用的注意事项
- C++学习(五)——string使用注意事项(一)
- C++注意事项
- keil 中使用c++的注意事项
- c/c++注意事项
- C++模板编写注意事项!!!dynamic initializer...
- VS 优化C++代码几点注意事项
- C#中调用C++写的DLL注意事项
- c++ 编程注意事项和知识点
- c++里的注意事项