1、构造函数
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值。构造函数没有返回值,可以有参数。
2、默认构造函数以及构造函数的重载
默认构造函数,如果不写的话也是可以的,编译器在执行时候发现类中没写构造函数,会自动给类中加上一个无参的构造函数,即默认构造函数。
但如果你自己编写了构造函数的重载,编译器识别到了存在构造函数,那么空参的构造函数就不会自动给你加上去, 所以建议手动写上默认构造函数。
#include<bits/stdc++.h>
using namespace std;
class A{
//类的外部接口
public:
A();//默认构造函数
A(string a,int age);//构造函数的重载
//成员函数
void get();
void set(string a,int age);
//类的成员数据 尽量隐蔽
private:
int age;
string name;
};
// 空参构造 成员数据初始化
A::A(){
this->age=19;
this->name="init_null";
}
//构造函数重载 传入参数 成员数据初始化
A::A(string a,int age){
this->age=age;
this->name=a;
}
//通过public 调用private的数据
void A::get(){
cout<<age<<" "<<name<<endl;
}
//通过public 更改private的数据
void A::set(string a,int age){
this->age=age;
this->name=a;
}
int main(){
A test;//创建一个午餐对象 会调用默认构造函数
test.get();
test.set("Mike",20);///通过成员函数修改成员数据
test.get();
A test1("test1",2);//传参用来初始化对象
test1.get();
return 0;
}
3、构造函数的默认值
#include<bits/stdc++.h>
using namespace std;
class A{
public:
A(string a="John",int age=10);//构造函数默认值
void get();
void set(string a,int age);
private:
int age;
string name;
};
A::A(string a,int age){
this->age=age;
this->name=a;
}
void A::get(){
cout<<age<<" "<<name<<endl;
}
void A::set(string a,int age){
this->age=age;
this->name=a;
}
int main(){
A test;//没有传入参数就会设为默认值
test.get();// 10 John
A test1("Bob");//只传入了第一个参数 ,后面没有的参数为默认值
test1.get();//10 Bob
A test2("Mike",20);//传入了所有参数
test2.get();//20 Mike
return 0;
}
特别注意,如果对于构造函数给予了默认值 那么就不可以写构造函数的重载
为什么? 如下的代码段,对于A test(“666”) 是会执行第一个还是第二个呢?
两者都满足 存在了多种情况 编译器不知道选择哪个, 所以会报错。
class A{
public:
A(string a="John",int age=10);//构造函数默认值
A(string b);
void get();
private:
int age;
string name;
};
4 、委托构造函数
这个很简单,就是有两个构造函数,其中委托构造函数把自己的初始化让另一个构造函数去执行罢了。
#include<bits/stdc++.h>
using namespace std;
class Clock{
public:
Clock(int h,int m,int s);
Clock();
void get();
private:
int hour,mintue,second;
};
//这种写法叫做 初始化列表
Clock::Clock(int h,int m,int s):hour(h),mintue(m),second(s){
}
///委托构造函数 将默认构造函数委托给了带有参数的构造函数去执行
Clock::Clock():Clock(0,0,0){
}
void Clock::get(){
cout<<hour<<":"<<mintue<<":"<<second<<endl;
}
int main(){
Clock myclockOne(14,22,30);
myclockOne.get();//14:22:30
Clock myclockTwo;
myclockTwo.get();//0:0:0
return 0;
}
5、复制构造函数(拷贝构造函数)
复制构造函数,就是用一个已有的对象,去初始化一个该类的对象。
复制构造函数被调用,有以下三种情况。
①用一个已有的对象,赋值给新创建的对象
其实就是把一个已有的复制一下给新创建的对象
Clock myclockOne;
Clock myclockTwo=myclockOne;
②调用函数时候,形参为该类的对象,主函数中的对象作为实参传入形参,形参把值拷贝下来后,就与实参断了联系 ,这里注意是值传递才会调用 ,引用传递不会。
因为值传递会调用复制构造函数,所以通常情况下,如果类比较复杂,建议用引用传递, 减少时间开销。
void fun1(Clock C){
}
int main(){
Clock myclockOne;
Clock myclockTwo=myclockOne;
fun1(myclockTwo);
return 0;
}
③函数返回值为类的对象时,会调用复制构造函数
这里为什么会调用呢?
表面上是将函数fun2()中的test对象返回给了主函数中的myclockOne,但是其实test是函数fun2()中的局部变量,test离开了函数fun2就消亡了。
这时候编译系统会在主函数创建一个无名的临时对象,这个临时对象的生命周期仅仅在myclockOne=fun2()这句话。调用函数fun2时,return test;这个语句,实际上是调用了复制构造函数,将test的值初始化给了临时对象,然后fun2()消亡,test对象也消亡了,但是临时对象存在于表达式myclockOne=fun2()中,计算完这个表达式后,临时对象就消失了。
Clock fun2(){
Clock test(1,2,3);
return test;
}
int main(){
Clock myclockOne;
myclockOne=fun2();
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int cnt;
class Clock{
public:
Clock(int h=0,int m=0,int s=0);
Clock(Clock &C);
void get();
private:
int hour,mintue,second;
};
Clock::Clock(int h,int m,int s):hour(h),mintue(m),second(s){
}
void Clock::get(){
cout<<hour<<":"<<mintue<<":"<<second<<endl;
}
//复制
Clock::Clock(Clock &C){
hour=C.hour;
mintue=C.mintue;
second=C.second;
cout<<"OK"<<" "<<++cnt<<endl;
}
void fun1(Clock C){
}
Clock fun2(){
Clock test(1,2,3);
return test;
}
int main(){
Clock myclockOne;
// 1、用一个对象去初始化该类的一个对象会调用 复制构造函数
Clock myclockTwo=myclockOne;
// 2、形参为该类的对象主函数,调用时候形参会先复制所以会调用
// 值传递会调用 引用传递不会
fun1(myclockTwo)
// 3、函数返回值是一个对象时候 会调用 至于这里为什么不输出?编译器优化了
// 详情见 https://www.cnblogs.com/cvwyh/p/10735019.html
myclockTwo=fun2();
return 0;
}
根据复制构造函数被调用的三种情况,上述代码应该会输出OK 1
OK 2
OK 3
但是并没有输出第三句话,其实这是因为编译器优化的原因,用cmd命令行运行,关掉编译器优化即可。
cmd运行c++的方法为 先跳转到cpp文件的文件夹
g++ 文件名.cpp -o 文件名字
然后运行 文件名.exe即可
关掉编译优化就是在编译命令最后加上 -fno-elide-constructors即可
如上图 第一次运行时没关编译器优化 第二次是关掉了编译优化