构造函数&析构函数

在类的初始化&清理时由编译器自动调用的函数,若开发者不写,则这两个函数内容为空;若开发者重写,则按照开发者写的函数来。两者语法及注意事项如下:

构造函数:类名(){}

主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

①没有返回值,也没有void。

②构造函数的函数名与类名相同。

③构造函数可以有参数,并且支持函数重载。

④程序在调用对象时会自动调用构造,无需手动调用,而且只会调用一次。

析构函数:~类名(){}

主要作用在于对象销毁前由系统自动调用,执行清理工作。

①析构函数没有返回值,也不写void。

②析构函数前面需要加一个波浪号。

③析构函数不可以有参数,也不支持函数重载。

④析构函数在程序执行结束前对象销毁时自动调用,不需要手动调用,且只会调用一次。

以下是两个的案例说明。通过对构造函数及析构函数的重写,展示①开发者重写两者时,会优先按照开发者重写的内容由编译器自动调用。②析构函数在不同的函数作用域中调用时的现象,说明确实是函数结束前对象销毁时自动调用的。

注意,①构造&析构函数若由开发者自定义,需要在类中写清楚权限为public。否则会报错。②构造&析构函数区分大小写,所以它们的名字需与类名一致。

代码:

#include

#include

using namespace std;

class Person {

public:

Person() {

cout<<"Person构造函数已调用。"<

}

~Person() {

cout << "Person析构函数已调用。" << endl;

}

};

int main() {

//此时构造函数会调用,但析构函数会在执行结束system("pause")时调用,转瞬即逝。

Person p;

system("pause");

return 0;

}

如图,运行结果:

此时构造函数会调用,但析构函数会在执行结束system(“pause”)时调用,转瞬即逝。故无法截图。

所以,有另一种方法,可以看到析构函数的调用,让类在不是main函数的其他函数中被创建,比如test函数,这样test函数一结束,就能看到析构函数的调用。

代码:

#include

#include

//#include"print.h"

//#include"point.h"

//#include"circle.h"

using namespace std;

class Person {

public:

Person() {

cout<<"Person构造函数已调用。"<

}

~Person() {

cout << "Person析构函数已调用。" << endl;

}

};

void test() {

Person p;

}

int main() {

//此时构造函数会调用,但析构函数会在执行结束system("pause")时调用,转瞬即逝。

//Person p;

test();

system("pause");

return 0;

}

如图,验证上述规则——析构函数在对象销毁时调用。

构造函数如何分类?

有两种需要掌握,①构造函数是如何分类的?②构造函数的调用方法有哪些?

先回答第一个问题,如何分类构造函数?有两种不同的分类方法,其一是根据构造函数有无参数分为无参构造(默认构造)与有参构造函数;其二是根据构造函数是否是拷贝构造函数。

先看第一个分类方法——无参构造与有参构造。

有参构造

构造函数是在对象的初始化时由编译器自动调用,对于一个类,它可以有成员函数,也可以有成员属性。所以,当有成员属性时,若对成员属性赋值,一种情况是,需要考虑是否是public权限?如果是,那么就可以直接new一个对象,并直接对象.成员属性=值;就可以。如果不是public权限,看类中是否有set属性()&get属性()方法。从这个场景出发,回到构造函数,这就是今天所说的另一种情况——在构造时对其成员属性赋值。此时就用到了有参构造。

无参构造

而无参构造即为没有参数的构造方法,也就是编译器默认的构造方法。

代码实现,通过有参构造,对private权限下的成员属性赋值,并对其读取,为了能够显示析构函数的调用,将代码主体部分放在了test()函数中。

在以下代码中,有三种构造函数的调用方式——意为初始化类为对象的方式。在后面会有说明三种调用方式语法及注意事项。

代码:

#include

#include

using namespace std;

class Person {

string person_name;

public:

Person() {

cout << "Person构造函数已调用." << endl;

}

//有参构造

Person(string &name) {

person_name = name;

cout << "Person有参构造函数已调用." << endl;

}

~Person() {

cout << "Person析构函数已调用." << endl;

}

string get_name() {

return person_name;

}

};

void test() {

//Person p;

//括号法有参构造函数调用

cout << "括号法有参构造函数调用." << endl;

string name = "张三";

//Person p1();//会被编译器认为是函数声明,返回值为Person类

Person p1(name);

//显式法有参构造函数调用

cout << "显式法有参构造函数调用." << endl;

Person p2 = Person(name);

//隐式法有参构造函数调用

cout << "隐式法有参构造函数调用." << endl;

Person p3 = name;

cout << "p1.name = " << p1.get_name() << endl;

cout << "p2.name = " << p2.get_name() << endl;

cout << "p3.name = " << p3.get_name() << endl;

}

int main() {

test();

system("pause");

return 0;

}

运行结果:

可以看到,用三种不同的构造函数调用方法进行了有参构造函数的调用。

写到这里,有一个问题:若构造函数中有多个参数,可以用这三种方法吗?

代码验证:

#include

#include

using namespace std;

class Person {

string person_name;

short int person_age;

public:

Person() {

cout << "Person构造函数已调用." << endl;

}

//有参构造

Person(string &name , short int &age) {

person_name = name;

person_age = age;

cout << "Person有参构造函数已调用." << endl;

}

~Person() {

cout << "Person析构函数已调用." << endl;

}

string get_name() {

return person_name;

}

short int get_age() {

return person_age;

}

};

void test() {

//Person p;

//括号法有参构造函数调用

cout << "括号法有参构造函数调用." << endl;

string name = "张三";

short int age = 18;

//Person p1();//会被编译器认为是函数声明,返回值为Person类

Person p1(name , age);

//显式法有参构造函数调用

cout << "显式法有参构造函数调用." << endl;

Person p2 = Person(name , age);

//隐式法有参构造函数调用

cout << "隐式法有参构造函数调用." << endl;

Person p3 = { name ,age };

cout << "p1.name = " << p1.get_name() << endl;

cout << "p2.name = " << p2.get_name() << endl;

cout << "p3.name = " << p3.get_name() << endl;

}

int main() {

test();

system("pause");

return 0;

}

这里的第44行花括号,chatGPT是这么解释的:

运行结果:

可以看到,多个参数也能接受,隐式调用也可以。

注意==》在第36行代码处,不能用括号法进行无参函数的构造函数调用,因为会被编译器认为是函数的声明,返回值是Person类。

拷贝构造函数

接着说构造函数的第二种分类方法——拷贝构造函数。

其实就是构造函数的参数现在变为了自己(当前理解2024年1月30日),变为自己时,需要注意,在参数这里,需要写明const 类 &对象 ,引用本身是个指针常量,再加上const修饰,意为完全地从另一个类身上复制到自己。

接着就是具体的代码实现。

代码:

#include

#include

using namespace std;

class Person {

string person_name;

short int person_age;

public:

Person() {

cout << "Person无参(默认)构造函数已调用." << endl;

}

//有参构造

Person(string &name , short int &age) {

person_name = name;

person_age = age;

cout << "Person有参构造函数已调用." << endl;

}

//拷贝构造

Person(const Person& person) {

person_name = person.person_name;

person_age = person.person_age;

cout << "Person拷贝构造函数已调用." << endl;

}

~Person() {

cout << "Person析构函数已调用." << endl;

}

string get_name() {

return person_name;

}

short int get_age() {

return person_age;

}

};

void test() {

//Person p;

//括号法有参构造函数调用

cout << "===============================" << endl;

cout << "括号法—调用—拷贝构造函数." << endl;

string name = "张三";

short int age = 18;

//Person p1();//会被编译器认为是函数声明,返回值为Person类

Person p1(name , age);

//显式法有参构造函数调用

cout << "===============================" << endl;

cout << "显式法—调用—拷贝构造函数." << endl;

Person p2 = Person(p1);

//隐式法有参构造函数调用

cout << "===============================" << endl;

cout << "隐式法—调用—拷贝构造函数." << endl;

Person p3 = p2;

cout << "===============================" << endl;

cout << "p1.name = " << p1.get_name() << "\tp1.age = " <

cout << "===============================" << endl;

cout << "p2.name = " << p2.get_name() << "\tp2.age = " << p2.get_age() << endl;

cout << "===============================" << endl;

cout << "p3.name = " << p3.get_name() << "\tp3.age = " << p3.get_age() << endl;

cout << "===============================" << endl;

}

int main() {

test();

cout << endl;

system("pause");

return 0;

}

运行结果:

三种调用构造函数方式

接下来说三种调用构造函数方式——对象初始化方式,分别是括号法,显式法,隐式法。

括号法

括号法:语法:类名 对象名(参数1,参数2,...,参数n);,此时,有参构造函数中有几个参数,就传几个参数。在有参构造函数中,最好参数写成引用传进去。这样,在外部修改时,对象中的成员属性也能够相应的修改。(此猜想错误,具体看运行结果)

代码:

#include

#include

using namespace std;

class Person {

string person_name;

short int person_age;

public:

Person() {

cout << "Person无参(默认)构造函数已调用." << endl;

}

//有参构造

Person(string &name , short int &age) {

person_name = name;

person_age = age;

cout << "Person有参构造函数已调用." << endl;

}

//拷贝构造

Person(const Person& person) {

person_name = person.person_name;

person_age = person.person_age;

cout << "Person拷贝构造函数已调用." << endl;

}

~Person() {

cout << "Person析构函数已调用." << endl;

}

string get_name() {

return person_name;

}

short int get_age() {

return person_age;

}

};

void test_v1() {

cout << "===============================" << endl;

cout << "有参构造函数的括号法调用." << endl;

string name = "张三";

short int age = 23;

//Person p1("hello ", 25);

Person p1(name, age );

cout << "===============================" << endl;

cout << "p1.name = " << p1.get_name() << "\tp1.age = " << p1.get_age() << endl;

age = 24;

name = "张三pro plus";

cout << "在外部修改后..." << endl;

cout << "===============================" << endl;

cout << "p1.name = " << p1.get_name() << "\tp1.age = " << p1.get_age() << endl;

}

int main() {

test_v1();

cout << endl;

system("pause");

return 0;

}

因为拷贝构造函数需要有其他对象才能拷贝,故在该段代码中没有体现。另外,无参构造函数不能有括号,也没有体现。所以,只有有参构造函数的括号法调用。

在这里发现,如果直接对其在括号内写入字符,会被编译器默认当为字符串数组,以及int类型,所以只能先定义,再写。或者在定义时就定义好与编译器认为的默认一致的情况。

运行结果:

发现推论错误,可能是因为类里面已经声明了,所以就算外部改了,里面也不会改。

==注意:==不能用括号法来调用无参构造函数,因为会被编译器认为是函数的声明,返回值是类。

显式法

第二种调用方式:显式法,语法:①有参构造函数

\rightarrow

→类 对象名 = 类(参数1,参数2,...,参数n);,②拷贝构造函数

\rightarrow

→类 对象名 = 类(对象);。

代码:

#include

#include

using namespace std;

class Person {

string person_name;

short int person_age;

public:

Person() {

cout << "Person无参(默认)构造函数已调用." << endl;

}

//有参构造

Person(string &name , short int &age) {

person_name = name;

person_age = age;

cout << "Person有参构造函数已调用." << endl;

}

//拷贝构造

Person(const Person& person) {

person_name = person.person_name;

person_age = person.person_age;

cout << "Person拷贝构造函数已调用." << endl;

}

~Person() {

cout << "Person析构函数已调用." << endl;

}

string get_name() {

return person_name;

}

short int get_age() {

return person_age;

}

};

//显式调用 构造函数的显式法调用.

void test_v2() {

string name = "张三";

short int age = 24;

cout << "===============================" << endl;

cout << "有参构造函数的显式法调用." << endl;

//有参构造函数的显式调用

Person p_orginal = Person(name, age);

//拷贝构造函数的显式调用

Person p_copy = Person(p_orginal);

cout << "===============================" << endl;

cout << "p_orginal.name = " << p_orginal.get_name() << "\tp_orginal.age = " << p_orginal.get_age() << endl;

cout << "===============================" << endl;

cout << "p_copy.name = " << p_copy.get_name() << "\tp_copy.age = " << p_copy.get_age() << endl;

}

int main() {

test_v2();

cout << endl;

system("pause");

return 0;

}

运行结果:

补充:匿名对象

显式法的右半部分,单独拿出来,是一个匿名对象——无名,只是显示法的左半部分是对象的名称。

特点:当前执行结束后,系统会立即回收匿名对象。

所以,有一点注意事项是,不能利用拷贝构造函数,初始化匿名对象。编译器会认为是对象声明,从而发生重定义的错误。

Person p_ori = {name , age};

Person p_after(p_ori);//❌

以上代码相当于Person (p_after) =Person p_ori; 与第一行的对象重定义。

隐式法

最后是隐式方法调用构造函数,因为构造函数有三种——无参,有参,拷贝,所以对应的隐式方法调用三种语法:无参:类 对象;(其实这种就是默认的构造函数调用,故不再代码中体现);有参:类 对象 = {参数1,参数2,...,参数n};,当有单个成员属性时,可以去掉大括号;拷贝:类 对象 = 对象;。此时的隐式方法调用构造函数相当于编译器自动改写:类 对象 = 类(参数1,参数2,...,参数n);以及类 对象 = 类(对象);。

以下是隐式方法调用构造函数的例子。

代码:

#include

#include

using namespace std;

class Person {

string person_name;

short int person_age;

public:

Person() {

cout << "Person无参(默认)构造函数已调用." << endl;

}

//有参构造

Person(string &name , short int &age) {

person_name = name;

person_age = age;

cout << "Person有参构造函数已调用." << endl;

}

//拷贝构造

Person(const Person& person) {

person_name = person.person_name;

person_age = person.person_age;

cout << "Person拷贝构造函数已调用." << endl;

}

~Person() {

cout << "Person析构函数已调用." << endl;

}

string get_name() {

return person_name;

}

short int get_age() {

return person_age;

}

};

//隐式方法调用构造函数

void test_v3() {

string name = "张三";

short int age = 23;

cout << "===============================" << endl;

cout << "有参构造函数的隐式法调用." << endl;

Person p_1 = { name , age };

Person p_2 = p_1;

cout << "===============================" << endl;

cout << "p_1.name = " << p_1.get_name() << "\tp_1.age = " << p_1.get_age() << endl;

cout << "===============================" << endl;

cout << "p_2.name = " << p_2.get_name() << "\tp_2.age = " << p_2.get_age() << endl;

}

int main() {

test_v3();

cout << endl;

system("pause");

return 0;

}

运行结果:

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。