C++中构造函数详解【超级详细】
时间:2024-04-28 19:10:19 来源:网络cs 作者:晨起 栏目:卖家故事 阅读:
1、构造函数的定义
主要作用于创建对象时为对象的成员属性进行赋值,构造函数由编译器自动调用,无需手动调用。
2、构造函数的基本语法
Person(int a){} --- 类名(参数列表){函数体}
3、构造函数的性质【重点】
构造函数的性质【重点内容】 | |
---|---|
1)构造函数无返回值也不需要添加void的;构造函数没有函数类型; | |
2)构造函数的函数名称要与类名一致; | |
3)构造函数具有形参列表,并且可以发生函数重载;【函数重载条件:同一作用域;函数名称相同;参数列表不一致(具体可见第一章第八节内容)】 | |
4)编译器会自动调用构造函数,无需进行手动调用,并且有且只有调用一次。 |
举例:
#include <iostream>using namespace std;class Person {public://发生了函数重载(假设第一个Person函数为Person1)Person(void){cout << "Person(void)" << endl;Age = 100;}//发生了函数重载(假设第二个Person函数为Person2)Person(int age);int GetAge(){return Age;}private:int Age;};Person::Person(int age){cout << "Person(int age)" << endl;Age = age;}int main(){class Person a;//当实例化对象是这种形式时,编译器调用Person1函数;当实例化对象是class Person a(99);编译器调用Person2函数cout << a.GetAge() << endl;return 0;}
运行结果:
Person(void)100
4、构造函数的重载分类以及调用【重点】
1)按照参数进行分类:有参构造函数和无参构造函数
格式:无参---类名(){} 有参---类名(参数列表){}举例:无参---Person(void){} 有参---Person(int a){}
2)按照类型进行分类:普通构造函数和拷贝构造函数
格式:普通---类名(){} 拷贝---类名(对象引用){}举例:普通---Person(int a){} 拷贝---Person(const Person &a){}
注:拷贝函数介绍
拷贝构造函数,具有一般构造函数的特性。主要可以实现用现有的对象完成对新建对象的赋值(复制操作),使用const修饰,表示只读,不能修改引用参数。
说的再多不如搞个程序:
#include <iostream>#include <string>using namespace std;class Person {public://无参构造函数Person(void){cout << "Person(void)" << endl;Age = 1;}//有参构造函数的声明Person(int age);Person(float age);//拷贝构造函数的声明Person(const Person& age);//普通成员函数int GetAge(void){return Age;}private:int Age;};//有参构造函数的定义Person::Person(int age){cout << "Person(int age)" << endl;Age = age;}//有参构造函数的定义Person::Person(float age){cout << "Person(float age)" << endl;Age = (int)age;}//拷贝构造函数的定义Person::Person(const Person& age)//注意这里是对象引用,相当于给实例化的对象起了一个别名{cout << "Person(const Person& age)" << endl;Age = age.Age;}int main(){float AGE = 3.14;cout << "无参构造函数!!!" << endl;class Person a;cout << "年龄为:" << a.GetAge() << endl;cout << "有参构造函数(int)!!!" << endl;class Person b(2);cout << "年龄为:" << b.GetAge() << endl;cout << "有参构造函数(float)!!!" << endl;class Person c(AGE);cout << "年龄为:" << c.GetAge() << endl;cout << "拷贝构造函数!!!" << endl;class Person d(a); //注意这里括号里面的值,a是前面创建的一个对象【通过a来初始化d对象】cout << "年龄为:" << d.GetAge() << endl;return 0;}
运行结果:
无参构造函数!!!Person(void)年龄为:1有参构造函数(int)!!!Person(int age)年龄为:2有参构造函数(float)!!!Person(float age)年龄为:3拷贝构造函数!!!Person(const Person& age)年龄为:1
3)如何将无参构造函数和有参构造函数结合成为一个?【使用默认参数的形式】
直接上程序:
#include <iostream>#include <string>using namespace std;class Person {public://普通构造函数的声明,并且使用默认参数形式,可以将无参构造与有参构造整合为一个Person(int age = 99);//普通成员函数int GetAge(void){return Age;}private:int Age;};Person::Person(int age){cout << "Person(int age)" << endl;Age = age;}int main(){class Person a;cout << "无参构造函数!!!" << endl;cout << "年龄为:" << a.GetAge() << endl;cout << "------------------------------------" << endl;class Person b(10);cout << "有参构造函数!!!" << endl;cout << "年龄为:" << b.GetAge() << endl;return 0;}
运行结果:
Person(int age)无参构造函数!!!年龄为:99------------------------------------Person(int age)有参构造函数!!!年龄为:10
5、构造函数的调用【重点】
1)括号法(最常使用的方法)
格式: class 类名 对象名(参数值);举例: class Person b(10); --- 这就是括号调用法
实例:
#include <iostream>#include <string>using namespace std;class Person {public://普通构造函数的声明Person(int age);Person(int age, int num);//拷贝构造函数的声明Person(const Person& a);//普通成员函数int GetAge(void){return Age;}int GetNum(void){return Num;}private:int Age;int Num;};//普通构造函数的定义Person::Person(int age){cout << "Person(int age)" << endl;Age = age;}Person::Person(int age, int num){cout << "Person(int age,int num)" << endl;Age = age;Num = num;}//拷贝构造函数的定义Person::Person(const Person& a){cout << "Person(const Person& a)" << endl;Age = a.Age;Num = a.Num;}int main(){class Person a(10); //这就是括号调用法cout << "a.Age:" << a.GetAge() << endl;cout << "--------------------------------" << endl;class Person b(11, 12);//这就是括号调用法cout << "b.Age:" << b.GetAge() << endl;cout << "b.Num:" << b.GetNum() << endl;cout << "--------------------------------" << endl;class Person c(b); //这就是括号调用法cout << "c.Age:" << c.GetAge() << endl;cout << "c.Num:" << c.GetNum() << endl;return 0;}
运行结果:
Person(int age)a.Age:10--------------------------------Person(int age,int num)b.Age:11b.Num:12--------------------------------Person(const Person& a)c.Age:11c.Num:12
2)显示法
格式: class 类名 对象名 = 类名(参数值);举例: class Person b = Person(10); --- 隐式对象也是对象
实例:
#include <iostream>#include <string>using namespace std;class Person {public://普通构造函数的声明Person(int age);Person(int age, int num);//拷贝构造函数的声明Person(const Person& a);//普通成员函数int GetAge(void){return Age;}int GetNum(void){return Num;}private:int Age;int Num;};//普通构造函数的定义Person::Person(int age){cout << "Person(int age)" << endl;Age = age;}Person::Person(int age, int num){cout << "Person(int age,int num)" << endl;Age = age;Num = num;}//拷贝构造函数的定义Person::Person(const Person& a){cout << "Person(const Person& a)" << endl;Age = a.Age;Num = a.Num;}int main(){class Person a = Person(10); //这就是显示调用法cout << "a.Age:" << a.GetAge() << endl;cout << "--------------------------------" << endl;class Person b=Person(11,12);//这就是显示调用法cout << "b.Age:" << b.GetAge() << endl;cout << "b.Num:" << b.GetNum() << endl;cout << "--------------------------------" << endl;class Person c = Person(b); //这就是显示调用法cout << "c.Age:" << c.GetAge() << endl;cout << "c.Num:" << c.GetNum() << endl;return 0;}
运行结果:
Person(int age)a.Age:10--------------------------------Person(int age,int num)b.Age:11b.Num:12--------------------------------Person(const Person& a)c.Age:11c.Num:12
3)隐式转化法
【注意:对于隐式转化法来说,多参数的不可用】。
格式: class 类名 对象名 = 值;举例: class person b = 10;
实例:
#include <iostream>#include <string>using namespace std;class Person {public://普通构造函数的声明Person(int age);Person(int age, int num);//拷贝构造函数的声明Person(const Person& a);//普通成员函数int GetAge(void){return Age;}int GetNum(void){return Num;}private:int Age;int Num;};//普通构造函数的定义Person::Person(int age){cout << "Person(int age)" << endl;Age = age;}Person::Person(int age, int num){cout << "Person(int age,int num)" << endl;Age = age;Num = num;}//拷贝构造函数的定义Person::Person(const Person& a){cout << "Person(const Person& a)" << endl;Age = a.Age;Num = a.Num;}int main(){class Person a =10; //这就是隐式转换法cout << "a.Age:" << a.GetAge() << endl;cout << "--------------------------------" << endl;//class Person b= (11,12); 隐式转换法不可以用于多参数。原因是","的运算符的运算规则只会返回最后一个表达式的值,因此不可以用于多参数//cout << "b.Age:" << b.GetAge() << endl;//cout << "b.Num:" << b.GetNum() << endl;//cout << "--------------------------------" << endl;class Person c = a; //这就是隐式转换法cout << "c.Age:" << c.GetAge() << endl;return 0;}
运行结果:
Person(int age)a.Age:10--------------------------------Person(const Person& a)c.Age:10
6、拷贝构造函数调用时机【重点】
下面三种情况拷贝构造函数会被自动调用:
拷贝构造函数被自动调用的三种情况: | |
---|---|
(1)使用已经创建的对象来初始化另外一个对象 | |
(2)使用已经创建的对象通过值传递的方式给函数参数赋值 | |
(3)以值的方式返回局部变量对象【这一种可能被编译器优化】 |
1)使用已经创建的对象来初始化另外一个对象
#include <iostream>#include <string>using namespace std;class Person {public://普通构造函数的声明Person(void);//拷贝构造函数的声明Person(const Person& c);//普通成员函数int GetAge(void){return Age;}private:int Age;};Person::Person(void){cout << "调用了普通构造函数!!!" << endl;Age = 1;//在这个构造函数中对于对象的成员属性进行初始化}Person::Person(const Person& c){cout << "调用了拷贝构造函数!!!" << endl;Age = c.Age;}int main(){Person a; //这一步编译器会自动调用构造函数来初始化a对象中的成员属性cout << "使用一个已经创建的对象来初始化另外一个对象!!!" << endl;Person b = a;//使用隐式转化法来调用拷贝构造函数,也可以使用括号法或者显示法进行调用cout << "b对象的年龄为:" << b.GetAge() << endl;return 0;}
运行结果:
调用了普通构造函数!!!使用一个已经创建的对象来初始化另外一个对象!!!调用了拷贝构造函数!!!b对象的年龄为:1
2)使用已经创建的对象通过值传递的方式给函数参数赋值(不太懂???没事看程序中的注释)
#include <iostream>#include <string>using namespace std;class Person {public://普通构造函数的声明Person(void);//拷贝构造函数的声明Person(const Person& c);//普通成员函数int GetAge(void){return Age;}private:int Age;};Person::Person(void){cout << "调用了普通构造函数!!!" << endl;Age = 1;//在这个构造函数中对于对象的成员属性进行初始化}Person::Person(const Person& c){cout << "调用了拷贝构造函数!!!" << endl;Age = c.Age;}void function(Person b)//这里的b是临时对象,当a传递过来时,相当于Person b = a;回调用拷贝构造函数{cout << "调用了普通函数!!!" << endl;}int main(){Person a; //这一步编译器会自动调用构造函数来初始化a对象中的成员属性cout << "使用已经创建的对象通过值传递的方式给函数参数赋值!!!" << endl;function(a); //这就相当于把对象a作为一个值传递给function函数的参数breturn 0;}
运行结果:
调用了普通构造函数!!!使用已经创建的对象通过值传递的方式给函数参数赋值!!!调用了拷贝构造函数!!!调用了普通函数!!!
3)以值的方式返回局部对象(可能会出现优化现象,看不到执行结果)
#include <iostream>#include <string>using namespace std;class Person {public://普通构造函数的声明Person(void);//拷贝构造函数的声明Person(const Person& c);//普通成员函数int GetAge(void){return Age;}private:int Age;};Person::Person(void){cout << "调用了普通构造函数!!!" << endl;Age = 1;//在这个构造函数中对于对象的成员属性进行初始化}Person::Person(const Person& c){cout << "调用了拷贝构造函数!!!" << endl;Age = c.Age;}Person fun(void){Person b; //创建一个b对象,这里会调用普通构造函数return b; //返回一个b对象}int main(){cout << "以值的方式返回局部变量对象!!!" << endl;Person d = fun(); //采用隐式转化法调用拷贝构造函数return 0;}
运行结果:该结果是存在问题的,结果应该也会出现"调用了拷贝构造函数!!!"这句话,造成这种情况的原因是不同编译器的优化问题。
以值的方式返回局部变量对象!!!调用了普通构造函数!!!
7、构造函数调用规则【重点】
1)创建一个类,C++编译器会给每一个类都添加至少3个函数:默认构造函数(空实现)、析构函数(空实现)以及拷贝构造函数(对象中的成员属性进行值拷贝(浅拷贝));
2)如果用户自己定义了有参构造函数,C++编译器不在提供默认无参构造函数,但是会提供默认拷贝构造函数;
3)如果用户自己定义了拷贝构造函数,C++编译器不会再提供其他构造函数【注意是不提供其他构造函数,析构函数还是有的】。
编译器原则:无参构造函数---->有参(非拷贝)函数---->拷贝构造函数【用户提供箭头右边的函数,编译器就不在提供其左边的所有函数】
具体实现过程如下:
(1)提供了一个有参构造函数
#include <iostream>#include <string>using namespace std;class Person {public:Person(int a){cout << "用户提供了有参构造函数!!!" << endl;Age = 1;}private:int Age;};int main(){Person a; //当用户没有提供有参构造函数,编译器会提供一个默认的无参构造函数 //当用户提供了有参构造函数,编译器将不再提供默认无参构造函数,因此程序会报错误。return 0;}
运行结果:
#include <iostream>#include <string>using namespace std;class Person {public:Person(int age){cout << "用户提供了有参构造函数!!!" << endl;Age = age;}int GetAge(void){return Age;}private:int Age;};int main(){Person a(10); //当用户没有提供有参构造函数,编译器会提供一个默认的无参构造函数 //当用户提供了有参构造函数,编译器将不再提供默认无参构造函数,因此程序会报错误。Person b = a; //但是编译器会提供默认的拷贝构造函数(值拷贝,也就是浅拷贝)cout << "b的年龄为:" << b.GetAge() << endl; return 0;}
运行结果:
用户提供了有参构造函数!!!b的年龄为:10
(2)提供了一个拷贝函数
#include <iostream>#include <string>using namespace std;class Person {public:Person(const Person& a) //用户自己提供一个拷贝构造函数{cout << "用户自己提供一个拷贝构造函数" << endl;Age = 1;}private:int Age;};int main(){Person a; //当用户提供了拷贝构造函数后,编译器不再提供其他构造函数return 0;}
运行结果:
8、浅拷贝与深拷贝【重点】
1)浅拷贝 --- 调用编译器提供的拷贝构造函数
浅拷贝简单来说,就是两个对象都指向同一地址空间,拷贝函数只对成员属性进行复制赋值。
#include <iostream>#include <string>#include <stdio.h>using namespace std;class Person {public://普通成员函数void GetP(void){p = (int*)malloc(sizeof(int));}int Data;int* p; //地址指针};int main(){Person a;a.Data = 10;a.GetP();*(a.p) = 20;cout << "a.Data:" << a.Data << endl;cout << "*(a.p):" << *(a.p) << endl;cout << "a.p:" << a.p << endl;cout << "------------------------------------" << endl;Person b = a; //自动调用拷贝构造函数cout << "b.Data:" << b.Data << endl;cout << "*(b.p):" << *(b.p) << endl;cout << "b.p:" << b.p << endl;cout << "------------------------------------" << endl;*(b.p)=99;cout << "*(a.p):" << *(a.p) << endl;return 0;}
运行结果:
a.Data:10*(a.p):20a.p:0000020005EE77E0------------------------------------b.Data:10*(b.p):20b.p:0000020005EE77E0------------------------------------*(a.p):99
浅拷贝存在的问题:
(1)一旦对b进行操作a的值也会发生变化;
(2)析构时,先对b进行析构,然后再析构a。但是由于b和a指向同一片空间,因此会导致一片空间进行了两次析构,发生了错误。【一定要主要这里两次析构会发生错误】
2)深拷贝 --- 用户自己编写拷贝构造函数
当构造函数中有堆区/自由存储区(new)开辟时,一定需要用户提供拷贝构造函数
#include <iostream>#include <string>#include <stdio.h>using namespace std;class Person {public://有参构造函数Person(int a, int b){Data = a;p = (int*)malloc(sizeof(int));*p = b;}//深拷贝构造函数Person(const Person& c){Data = c.Data;p = (int*)malloc(sizeof(int));*p = *(c.p);}//普通成员函数用于释放申请的空间void Free(void){free(p);}int Data;int* p; //地址指针};int main(){Person a = Person(10, 20); //调用构造函数,进行初始化cout << "a.Data:" << a.Data << endl;cout << "*(a.p):" << *(a.p) << endl;cout << "a.p:" << a.p << endl;cout << "----------------------------------" << endl;Person b = a;//调用深拷贝构造函数cout << "b.Data:" << b.Data << endl;cout << "*(b.p):" << *(b.p) << endl;cout << "b.p:" << b.p << endl;cout << "----------------------------------" << endl;*(b.p) = 99;cout << "*(a.p):" << *(a.p) << endl;cout << "*(b.p):" << *(b.p) << endl;return 0;}
运行结果: 可以看出p的地址明显不同
a.Data:10*(a.p):20a.p:0000021BEEDC1C10----------------------------------b.Data:10*(b.p):20b.p:0000021BEEDC14C0----------------------------------*(a.p):20*(b.p):99
阅读本书更多章节>>>>
本文链接:https://www.kjpai.cn/gushi/2024-04-28/163324.html,文章来源:网络cs,作者:晨起,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!