1、构造函数可以重载,也可以带有缺省参数
例如如下这样,都构成重载,编译器会自动选择符合的构造函数
string(void)
string(const sting&str);
string(const string&str,size_t len=5);
string(const char*s);
2、缺省构造函数(无参构造函数)
①如果类中自己没有定义构造函数,那么编译器会为该类提供一个缺省的无参构造函数
->对于基本类型的成员变量不做初始化
->对于类类型的成员变量(成员子对象),会自动调用相应类的无参构造函数来初始化
②如果类中自己定义了构造函数,无论是否有参数,编译器都不会在提供缺省的无参构造函数
3、类型转换构造函数
explicit:显示的告诉编译器进行显示转换,使用它修饰的构造函数,强制要求类型转换的过程必须显式完成
格式:explicit 类名(源类型){}
例如:
4、拷贝构造函数
①用一个已经存在的对象作为同类的构造实参,创建新的副本对象,会调用该类的拷贝构造函数
格式 :类名(const 类名&){....}
比如:A a2(a1)这时匹配的是A类的拷贝构造函数
②如果一个类没有显示定义拷贝的构造函数,那么编译器会为该类提供一个缺省的拷贝构造函数
-》对于基本类型的成员,按字节复制
-》对类类型的成员变量(成员子对象),自动调用相应类的拷贝构造函数完成拷贝初始化
对象之间的自洽性,会自动寻找洽合的对象
实际上编译器提供的缺省拷贝函数已经很好用了,我们很少自己写拷贝函数
③拷贝构造函数的调用时机
-》用以定义的对象作为头类对象的构造实参
-》以对象的形式向函数传递参数
-》从函数中返回对象(有可能被编译器优化掉)
5、 初始化列表
①语法
格式:类名(形参表):成员变量1(初值),成员变量2(初值)....{...}
②大多数情况下,使用初始化列表和原来在构造函数中赋初值结果相同,两种方式可以任选,但是有些特殊场景必须要使用初始化列表
-》如果有类类型的成员变量,并希望以有参的方式对其初始化,则必须采用初始化列表指明需要的构造实参
-》如果有引用型或const修饰的成员变量,必须使用初始化对象来显式的初始化
注:初始化顺序由声明顺序决定,而与初始化列表无关,所以最好不要使用一个成员变量去初始化另一个成员变量
1、this指针
①在类中的成员函数(包括构造函数、析构函数)中都会隐藏一个该类类型的指针参数this,这就是为什么成员函数访问类中的其他成员是可以直接访问的原因,本质就是靠this指针来实现的
② 对于普通成员函数,this指向调用对象的地址,如果对于构造函数,this指向正在创建的地址
③大多数情况下其实是可以忽略this指针的,在成员函数中直接访问类中的其他成员即可,但是以下几种特殊的情况必须使用this指针
-》区分作用域
当参数变量与成员变量名字相同时,可以通过this指针区分,用this指针访问的一定是成员变量
-》从成员函数返回调用对象本身
-》从类的内部销毁对象自身,堆区new的对象,对象自销毁
-》作为成员的函数实参,实现不同对象的交互,很少使用,因为代码不好维护,不会希望对象与对象之间有过多的依赖和关联,了解一下即可
2、常成员函数
①在一个类中的普通成员函数参数表后面加const,这个成员函数就是常成员函数(常函数)
格式:返回类型 函数名(形参表)const{函数体}
②常成员函数中this指针是一个常指针,不能在常成员函数中直接修改成员变量的值
注:const实际上修饰了this指针,使它成为了一个常量指针,就无法对只读对象进行修改
但是我又想改变这个参数怎么办呢?
可以进行去常处理const_cast
也可以在想修改的成员变量前加mutable,也相当于去常
③非常对象既可调用非常函数也可以调用常函数,但常对象(包括常指针)只能调用常函数,不可调用非常函数
void func(int*p){}
int main(void){
const int a=100;
func(&a);//报错,const int*—>int* 相当于放大权限
}
void func(const int *p){}
int main(void){
int a=100;
func(&a);//可以,int*->const int* 缩小权限可以
}
④同一个类中,函数名和参数表相同的成员,常版本和非常版本可以构成重载关系,常对象匹配常版本,非常对象匹配非常版本