C++中相比较C中引入了类的概念。创建一个类就必然会用到构造函数
下面来介绍C++中的构造函数的相关知识
一.构造函数的分类和调用
假设定义一个如下的类
class Person{
private:
protected:
public:
/*
其他构造方法后面给出
*/
~Person(){
cout<<"析构函数"<<endl;
}
double money;
};
①.无参构造函数(默认构造方法,下面给出的代码是我自定义的,默认构造方法是空的)
Person(){
cout<<"无参构造函数"<<endl;
}
②.有参构造函数
1.普通构造函数
Person(double newMoney){
money = newMoney;
cout<<"有参构造函数"<<endl;
}
2.拷贝构造函数(可以观察到拷贝构造函数是采取引用传递的方式,且不允许修改传入对象的值)
Person(const Person & oldMan){
money = oldMan.money;
cout<<"拷贝构造函数"<<endl;
}
二.构造函数的调用
①.隐式法
Person youngManOne;//调用默认构造方法
Person youngManTwo(1000);//调用有参构造方法
Person youngManThree(youngManTwo);//调用拷贝构造方法
注意:不能用 Person p();这样编译器会认为这是一个函数声明即返回Person类对象的函数
此时还可以用匿名对象,匿名对象在本行代码执行结束后就会释放给出示例
int main(){
Person(1000);//匿名式声明
cout<<"????"<<endl;
}
执行结果如下,证明上述结论是正确的。
在使用匿名对象时不能使用拷贝构造函数 如果写成 Person (p1) 这种写法等价于 Person p1 对于这个来说明显是一个对象的声明,所以不可以当做一个匿名对象来使用。
②.显式法(类似Java)
Person youngManOne = Person();//调用默认构造方法
Person youngManTwo = Person(1000);//调用有参构造方法
Person youngManThree =Person(youngManTwo);//调用拷贝构造方法
③.隐式类型转换(不常用)
Person p1 = 100;//等价于 Person p1 = Person(100);
Person p2 = p1//等价于 Person p2 = Person(p1);
三.拷贝构造函数调用时机
①.用已经创建好的对象来初始化新的对象(就是上面调用拷贝构造方法的示例)
②.以值传递的方式给函数参数传值
下面是测试代码
void work(Person oldMan){
}
void test2(){
Person oldMan;
work(oldMan);
}
int main(){
test2();
}
测试结果
也就是说先调用无参构造方法创建了一个对象,后来调用work函数后才执行了拷贝构造函数。也就说明以值传递的方式给函数参数传值此时会调用拷贝构造方法。
③.以值方式返回局部对象
测试代码
Person doWork(){
Person youngMan;
return youngMan;
}
void test3(){
Person youngMan = doWork();
}
int main(){
test3();
}
测试结果
说明同上。证明结果正确。
下面有需要记住的几点:
- 如果提供了有参的构造,那么系统就不会提供默认的构造了,但是会提供拷贝构造
- 如果提供了拷贝构造函数,那么系统就不会提供其他的构造函数
四.初始化列表式构造函数
构造函数语法:类名(构造函数的参数列表):属性(值or参数), 属性(值or参数)
例子:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
class Number{
public :
int aNum;
int bNum;
double cNum;
Number():aNum(10),bNum(30),cNum(50){
}
Number(int a, int b, double c):aNum(a),bNum(b),cNum(c){
}
};
int main(){
using namespace std;
Number num1;
cout << "num1---- aNum:" << num1.aNum << "bNum:" << num1.bNum << "cNum:" << num1.cNum << endl;
Number num2(1000,4000,8000);
cout << "num2---- aNum:" << num2.aNum << "bNum:" << num2.bNum << "cNum:" << num2.cNum << endl;
system("pause");
return EXIT_SUCCESS;
}
执行结果:
证明初始化列表式是能够对类中的属性进行赋值的。
五.类对象作为成员的案例
代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Phone{
public:
string name;
Phone(){
cout << "Phone无参构造函数" << endl;
}
Phone(string newName):name(newName){
cout << "Phone有参构造函数" << endl;
}
~Phone(){
cout << "Phone析构函数" << endl;
}
};
class App{
public:
string name;
App(){
cout << "App无参构造函数" << endl;
}
App(string newName){
name = newName;
cout << "App有参构造函数" << endl;
}
~App(){
cout << "App析构函数" << endl;
}
};
class Student{
public:
App app;
Phone phone;
string realName;
Student(){
cout << "Student无参构造函数" << endl;
}
Student(string appName, string phoneName, string stuRealName) :app(appName),phone(phoneName),realName(stuRealName){
cout << "Student有参构造函数" << endl;
}
~Student(){
cout << "Student析构函数" << endl;
}
};
void use(){
Student stu("FaceBook", "8848", "张喜文");
cout << stu.realName << "在" << stu.phone.name << "上使用" << stu.app.name << endl;
}
int main(){
use();
system("pause");
return EXIT_SUCCESS;
}
执行结果:
执行分析:
可以观察出若一个类中有其他类的对象,构造顺序是先构造其他类的对象,然后构造自己,析构函数则是先执行本类中的析构函数,最后再执行属性类的析构函数。即析构执行顺序与构造的顺序相反
explicit关键字
作用:防止构造函数中的隐式类型转换
例子:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
class Number{
public:
int aNum;
Number(){
}
explicit Number(int num){
aNum = num;
}
};
int main(){
Number num = 1;
}
执行后结果:证明上述结论正确