构造函数&析构函数
在类的初始化&清理时由编译器自动调用的函数,若开发者不写,则这两个函数内容为空;若开发者重写,则按照开发者写的函数来。两者语法及注意事项如下:
构造函数:类名(){}
主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
①没有返回值,也没有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; } 运行结果: 相关文章
发表评论