C++中构造函数详解【超级详细】
不爱敲代码~ 2024-10-20 14:35:01 阅读 61
1、构造函数的定义
主要作用于创建对象时为对象的成员属性进行赋值,构造函数由编译器自动调用,无需手动调用。
2、构造函数的基本语法
<code>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)括号法(最常使用的方法)
<code>格式: 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:11
b.Num:12
--------------------------------
Person(const Person& a)
c.Age:11
c.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:11
b.Num:12
--------------------------------
Person(const Person& a)
c.Age:11
c.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函数的参数b
return 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;
}
运行结果:
<code>#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)浅拷贝 --- 调用编译器提供的拷贝构造函数
浅拷贝简单来说,就是两个对象都指向同一地址空间,拷贝函数只对成员属性进行复制赋值。
<code>#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):20
a.p:0000020005EE77E0
------------------------------------
b.Data:10
*(b.p):20
b.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):20
a.p:0000021BEEDC1C10
----------------------------------
b.Data:10
*(b.p):20
b.p:0000021BEEDC14C0
----------------------------------
*(a.p):20
*(b.p):99
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。