在类的初始化&清理时由编译器自动调用的函数,若开发者不写,则这两个函数内容为空;若开发者重写,则按照开发者写的函数来。两者语法及注意事项如下:
类名(){}
主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
①没有返回值,也没有void。
②构造函数的函数名与类名相同。
③构造函数可以有参数,并且支持函数重载。
④程序在调用对象时会自动调用构造,无需手动调用,而且只会调用一次。
~类名(){}
主要作用在于对象销毁前由系统自动调用,执行清理工作。
①析构函数没有返回值,也不写void。
②析构函数前面需要加一个波浪号。
③析构函数不可以有参数,也不支持函数重载。
④析构函数在程序执行结束前对象销毁时自动调用,不需要手动调用,且只会调用一次。
以下是两个的案例说明。通过对构造函数及析构函数的重写,展示①开发者重写两者时,会优先按照开发者重写的内容由编译器自动调用。②析构函数在不同的函数作用域中调用时的现象,说明确实是函数结束前对象销毁时自动调用的。
注意,①构造&析构函数若由开发者自定义,需要在类中写清楚权限为public
。否则会报错。②构造&析构函数区分大小写,所以它们的名字需与类名一致。
代码:
#include
#include
using namespace std;
class Person {
public:
Person() {
cout<<"Person构造函数已调用。"<<endl;
}
~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构造函数已调用。"<<endl;
}
~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 = " <<p1.get_age() << endl;
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;
}
运行结果: