Skip to content

构造函数

构造函数,是一个比较特殊的函数。我们在使用一个类的对象的时候,需要为其分配空间。空间分配完成之后,我们一般都会对创建的对象的属性进行初始化的操作。而这个过程就可以在构造函数中来完成了。

因此:构造函数是一个函数

  • 触发时机:在对象创建的时候触发
  • 作用:对对象的属性进行初始化的赋值操作

1.构造函数定义

  • 构造函数的名字,必须和类的名字相同
  • 构造函数不能写返回值类型
  • 构造函数可以有不同的重载
  • 构造函数需要公开权限 public
c++
#include<iostream>
using namespace std;

class Person {
public:
	Person() {
		cout << "我在创建对象时被调用,并且没有参数" << endl;
	}

	// 重载
	Person(int age) {
        
		cout << "我在创建对象时被调用,参数为"<< age << endl;
	}
};

注意

  • 如果我们没有在一个类中写任何的构造函数,系统会为其添加一个public权限的无参构造函数,可以让我们创建对象
  • 如果我们给一个类写构造函数了,此时系统将不再提供任何默认的构造函数。

2.构造函数调用

2.1 显式调用

通过类名 对象名 = 构造函数(参数)语法去声明并调用类的构造函数

c++
#include<iostream>
using namespace std;

class Person {
public:
	Person() {
		cout << "我在创建对象时被调用,并且没有参数" << endl;
	}

	// 重载
	Person(int age) {
		cout << "我在创建对象时被调用,参数为"<< age << endl;
	}
};


int main() {
	Person p1 = Person();
	Person p2 = Person(18);
	return 0;
}

以上调用方法可以使用以下缩写

c++
Person p1;
Person p2(18);

image-20240718110007335

注意

通过缩写的方式,若调用无参的构造函数去创建对象,不能写小括号()

若写了小括号,则会被认为声明了一个返回类型为类名(Person)的函数

c++
Person p2();

2.2 隐式调用

隐式调用可以不写构造函数的名字,直接把参数写在一堆大括号{}

  • 此时系统会根据大括号中的实参的数量和类型,找到与之匹配的构造函数调用
  • 若只有一个参数,可以省略大括号
c++
#include<iostream>
using namespace std;

class Person {
public:
	Person() {
		cout << "我在创建对象时被调用,并且没有参数" << endl;
	}

	// 重载
	Person(int age) {
		cout << "我在创建对象时被调用,参数为"<< age << endl;
	}
    
    Person(int age,string name) {
		cout << "我在创建对象时被调用,参数为"<< age <<"名字为"<< name << endl;
	}
};


int main() {
	Person p1 = {};
	Person p2 = 18;
    Person p3 = {18,"xiazhi"};
	return 0;
}

3.explicit

explicit是用来修饰构造函数的,写在构造函数之前。表示无法通过隐式调用来访问这个构造函数

c++
#include<iostream>
using namespace std;

class Person {
public:
	Person() {
		cout << "我在创建对象时被调用,并且没有参数" << endl;
	}

	// 重载
	explicit Person(int age) {
		cout << "我在创建对象时被调用,参数为"<< age << endl;
	}
    
    Person(int age,string name) {
		cout << "我在创建对象时被调用,参数为"<< age <<"名字为"<< name << endl;
	}
};


int main() {
	Person p1 = {};
	Person p2 = 18;
    Person p3 = {18,"xiazhi"};
	return 0;
}

image-20240718112134017

4.初始化列表

构造函数的初衷就是用来对对象的属性进行初始化的赋值操作

我们可以在创建对象时,给某些数据一些指定的值,这个值可以是我们通过构造函数参数传入,也可以写一个字面量的值

c++
#include<iostream>
using namespace std;

class Person {
public:
	string name;
	int age;
	string sex;
	double tall;
	string hobby;
	string health;

	Person(string n,int a,string s,double t,string h) {
		name = n;
		age = a;
		sex = s;
		tall = t;
		hobby = h;
		health = "健康";
	}
};


int main() {
	Person p1("xiazhi",18,"男",181.00,"唱,跳,rap,篮球");
	return 0;
}

TIP

在成员函数中需要访问成员变量,可以直接写变量名,这样就出现一个问题,构造函数的名称和成员变量的名称不能写成一样。鉴于此,后面会学习到this的知识

如果构造函数的参数过多,我们需要一行一行的给成员属性赋值,为了方便,c++提供了构造函数的初始化列表

  • 在构造函数形参括号的后面写一个:,后面通过成员变量名(值)的方式直接给成员变量赋值,多个变量使用逗号,隔开
c++
#include<iostream>
using namespace std;

class Person {
public:
	string name;
	int age;
	string sex;
	double tall;
	string hobby;
	string health;

	Person(string n, int a, string s, double t, string h) :name(n), age(a), sex(s), tall(t), hobby(h), health("健康") {
		// ...
	}
};

int main() {
	Person p1("xiazhi", 18, "男", 181.00, "唱,跳,rap,篮球");
	cout << p1.name << endl;		// xiazhi
	cout << p1.age << endl;			// 18
	cout << p1.sex << endl;			// 男
	cout << p1.tall << endl;		// 181.00
	cout << p1.hobby << endl;		// 唱,跳,rap,篮球
	cout << p1.health << endl;		// 健康
	return 0;
}

通过构造函数的初始化列表,也可以将构造函数的形参名称写成和成员变量一样

c++
#include<iostream>
using namespace std;

class Person {
public:
	string name;
	int age;

	Person(string name, int age) :name(name), age(age) {
		// ...
	}
};

int main() {
	Person p1("xiazhi", 18);
	cout << p1.name << endl;		// xiazhi
	cout << p1.age << endl;			// 18

	return 0;
}

5.拷贝构造函数

5.1 语法

当已经创建一个对象时,可以直接把这个对象赋值给另一个对象,这样就完成了构造函数的拷贝,并且两个对象中的数据处于不同地址,不会冲突

c++
#include<iostream>
using namespace std;

class Person {
public:
	string name;
	int age;

	Person(string name, int age) :name(name), age(age) {
		// ...
	}

};

int main() {
	Person p1("xiazhi", 18);

	Person p2 = p1;
	cout << p1.name << endl;
	cout << p2.name << endl;

	cout << &p1.name << endl;
	cout << &p2.name << endl;
	return 0;
}

image-20240718154410860

5.2 手写拷贝

c++
class Person {
public:
	string name;
	int age;

	Person(string name, int age) :name(name), age(age) {
		// ...
	}

	Person(const Person& p) :name(p.name), age(p.age) {
		cout << "拷贝构造函数被调用了" << endl;
		
		// 拷贝构造的其他逻辑...
	}
};


int main() {
	Person p1("xiazhi", 18);

	Person p2(p1);
    
	cout << p1.name << endl;
	cout << p2.name << endl;

	cout << &p1.name << endl;
	cout << &p2.name << endl;
	return 0;
}

image-20240718155007567

6.匿名对象

匿名对象(也称临时对象)

  1. 临时对象:匿名对象通常是临时创建的,用于某个特定的操作或任务,不需要在程序中保留其状态或引用。
  2. 作为函数参数:匿名对象可以作为函数的参数传递,这样可以简化代码,避免创建额外的对象。
  3. 作为函数返回值:函数可以返回匿名对象,这样可以在调用函数时直接得到临时对象的值。
  4. 生命周期:匿名对象的生命周期通常很短,只在当前表达式或语句中有效(当前行)。一旦表达式或语句结束,匿名对象就会被销毁。因此,在使用匿名对象时要注意其生命周期,避免出现悬空指针或未定义行为。

6.1 创建匿名对象

产生匿名对象的三种情况

  • 以值的方式给函数传参;
    • Cat():生成了一个匿名对象,执行完Cat( )代码后,此匿名对象就此消失。这就是匿名对象的生命周期。
    • Cat cc = Cat():首先生成了一个匿名对象,然后将此匿名对象变为了cc对象,其生命周期就变成了cc对象的生命周期。
  • 类型转换
  • 函数需要返回一个对象时;return temp;

MIT Licensed