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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。