一、 构造函数和初始化表
1、构造函数可以重载
1)构造函数通过参数表的差别化形成重载关系,创建对象时通过构造实参类型选择匹配,表示不同的对象创建方式
2)可以适当使用缺省参数,减少构造函数重载版本数量,但注意不要和缺省参数构造歧义错误。
1#include<iostream>
2using namespace std;
3class student{
4public:
5 student(const string&name,int age,int no){
6 m_name=name;
7 m_age=age;
8 m_no=no;
9 }
10 student(const string& name){
11 m_name=name;
12 m_age=0;
13 m_no=0;
14 }
15 student(void){
16 m_name="无名";
17 m_age=0;
18 m_no=0;
19 }
20 void who(void){
21 cout<<"我叫"<<m_name<<",今年"<<m_age<<"岁"<<endl;
22 cout<<"学号"<<m_no<<endl;
23 }
24 private:
25 string m_name;
26 int m_age;
27 int m_no;
28};
29int main(void){
30 student s1("张飞",25,10086);
31 s1.who();
32 student s2("赵云");
33 s2.who();
34 student s3;//不要写小括号
35 //student s3=student();//OK 等价于上句
36 s3.who();
37 return 0;
38 }
等价于:
1#include<iostream>
2using namespace std;
3class student{
4public:
5 student(const string&name="无名",intage=0,int no=0){
6 m_name=name;
7 m_age=age;
8 m_no=no;
9 }
10#if 0
11 student(const string& name){
12 m_name=name;
13 m_age=0;
14 m_no=0;
15 }
16 student(void){
17 m_name="无名";
18 m_age=0;
19 m_no=0;
20 }
21#endif
22 void who(void){
23 cout<<"我叫"<<m_name<<",今年"<<m_age<<"岁"<<endl;
24 cout<<"学号"<<m_no<<endl;
25 }
26private:
27 string m_name;
28 int m_age;
29 int m_no;
30};
31int main(void){
32 student s1("张飞",25,10086);
33 s1.who();
34 student s2("赵云");
35 s2.who();
36 student s3;//不要写小括号
37 //student s3=student();//OK 等价于上句
38 s3.who();
39 return 0;
40 }
2、缺省构造函数
1) 如果一个函数没有定义构造函数,编译器会提供一个缺省的无参构造函数,使成员变量获得定义。
2) 对于基本类型的成员不做初始化
3) 对于类类型的成员变量(成员子对象),调用相应类型的无参构造函数初始化
1#include<iostream>
2using namespace std;
3class teacher{
4public:
5 void who(void){
6 cout<<m_name<<','<<m_age<<endl;
7 }
8private:
9 //缺省构造函数,调用string类无参构造函数初始化
10 string m_name(成员子对象);
11 int m_age;//不做初始化
12};
13int main(void){
14 teacher t;
15 t.who();
16 return 0;
17 }
tarena@tarena-virtual-machine:~/day39$./a.out
,-1217916940
4) 如果定义任何构造函数,编译器都不会再提供缺省无参构造函数
1#include<iostream>
2using namespace std;
3class teacher{
4public:
5 //teacher(void){}缺省无参构造函数
6 teacher(const string&name){
7 m_name=name;
8 m_age=0;
9 }
10 void who(void){
11 cout<<m_name<<','<<m_age<<endl;
12 }
13private:
14 //缺省构造函数,调用string类无参构造函数初始化
15 string m_name;
16 int m_age;//不做初始化
17};
18int main(void){
19 teacher t;
20 t.who();
21 return 0;
22 }
执行:错误: 对‘teacher::teacher()’的调用没有匹配的函数
3、类型转换构造函数(单参构造函数)
基本类型-à类类型
类类型à类类型
Class 目标类型{
目标类型(const 源类型& 源目标类型)
}
在目标类中,可以接收单个源类型的对象的构造函数,支持从源类型到目标类型的隐式转换
使用explicit关键字,可以强制这种转换必须显式进行
举例:
1#include<iostream>
2using namespace std;
3class integer{
4public:
5 integer(void){//无参构造函数
6 cout<<"integer::integer(void)"<<endl;
7 m_data=0;
8 }
9 /* 实现int-->integer:类型转换构造函数 */
10 //ecplicit 作用是告诉编译器不能做隐式类型转换
11 explicit integer(const int& data){//有参构造函数
12 cout<<"integer::integer(int)"<<endl;
13 m_data=data;
14 }
15 void print(void){
16 cout<<m_data<<endl;
17 }
18private:
19 int m_data;
20};
21int main(void){
22 integer i;
23 i.print();//0
24 //i=integer(100);
25 // i=100;//隐式类型转换int --> integer
26 i.print();//100
27 //显示类型转换
28 i=integer(200);//建议使用C++这种
29 i=(integer)200;
30 i=static_cast<integer>(200);
31 i.print();//200
32 }
4、拷贝构造函数
1) 用一个已经存在的对象构造同类型的副本对象,会调用该类型的拷贝构造函数。
类名 (const 类名& that){ … }
Class A{ };
A a1;
A a2(a1);//调用拷贝构造函数
例:
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(int data=0){
6 cout<<"A::A(int=0)"<<endl;
7 m_data=data;
8 }
9 A(const A& that){
10 cout<<"拷贝构造函数"<<endl;
11 m_data=that.m_data;
12 }
13
14 int m_data;
15};
16int main(void){
17 Aa1(100);
18 // Aa2(a1);
19 Aa2=a1;//和上面等价
20 cout<<a1.m_data<<endl;//100
21 cout<<a2.m_data<<endl;//100
22 return 0;
23 }
tarena@tarena-virtual-machine:~/day39$./a.out
A::A(int=0)
100
100
2) 如果一个类没有定义拷贝构造函数,那么编译器会为其提供一个缺省的构造函数:
-à对基本类型的成员变量,按字节赋值
à对类类型的成员变量(成员子对象),调用相应类的拷贝构造函数
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(int data=0){
6 cout<<"A::A(int=0)"<<endl;
7 m_data=data;
8 }
9 int m_data;
10};
11int main(void){
12 Aa1(100);
13// A a2(a1); //调用缺省的构造函数
14 Aa2=a1;//和上面等价
15 cout<<a1.m_data<<endl;//100
16 cout<<a2.m_data<<endl;//100
17 return 0;
18 }
3) 如果自己定义了拷贝构造函数,编译器将不再提供缺省的拷贝构造函数,这时要实现成员复制相关的工作,必须在自己定义的拷贝构造函数中编写代码完成
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(int data=0){
6 cout<<"A::A(int=0)"<<endl;
7 m_data=data;
8 }
9 A(const A& that){
10 cout<<"拷贝构造函数"<<endl;
11 m_data=that.m_data;
12 }
13
14 int m_data;
15};
16class B{
17public:
18 A m_a;
19};
20int main(void){
21 Bb;
22 b.m_a.m_data=123; //拷贝A的m_a的值是由A类中的拷贝构造函数完成
23 Bb2(b);//调用拷贝构造函数
24 cout<<b.m_a.m_data<<endl;//123
25 cout<<b2.m_a.m_data<<endl;//123
26 return 0;
27 }
A::A(int=0)
拷贝构造函数
123
123
4) 拷贝构造函数调用的时机
à用已经存在的对象,构造一个同类型的不存在的对象 A a2(a1);
à以对象的形式向函数传递参数(有时会被编译器优化掉)
注:一般情况不需要自己定义拷贝构造函数,因为缺省拷贝构造函数已经很好用了。
void func(A a){….}
int main(void){
Aa1;
func(a1)
}
举例:
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(void){
6 cout<<"A::A()"<<endl;
7 }
8 A(const A& that){
9 cout<<"A::A(const A&)"<<endl;
10 }
11};
12void foo(A a){ }
13 Abar(void){
14 Aa;
15 cout<<&a<<endl;
16 return a;
17 }
18int main(void){
19 Aa1;
20 //用已定义的对象作为同类型对象的构造实参
21 Aa2(a1); //构造一次
22 //以对象的形式向函数传递参数
23 foo(a1); //拷贝构造一次
24 /*bar函数返回a会拷贝到一个匿名对象中,发生一次拷贝构造,匿名对象拷贝给a3 ,再次一次拷贝构造*/
25 /*因为编译器优化,可能不做拷贝*/
26 /**防止g++优化选项 -fno-elide-constructors*/
27 Aa3=bar(); //拷贝构造两次
28 cout<<&a3<<endl;
29 return 0;
30 }
tarena@tarena-virtual-machine:~/day39$ g++6.cpp -fno-elide-constructors
tarena@tarena-virtual-machine:~/day39$./a.out
A::A()
A::A(const A&)
A::A(const A&)
A::A()
0xbff1be4f
A::A(const A&)
A::A(const A&)
0xbff1be7e
tarena@tarena-virtual-machine:~/day39$ g++6.cpp
tarena@tarena-virtual-machine:~/day39$./a.out
A::A()
A::A(const A&)
A::A(const A&)
A::A()
0xbffa907f
0xbffa907f
//调用两次无参构造,调用四次拷贝构造函数
笔试题:
自定义构造函数 编译器会提供
无 缺省无参构造、缺省拷贝构造
非拷贝构造函数 缺省拷贝构造
拷贝构造函数 无
5、初始化表
1) 指定类中 变量的初始化方式
类名(形参表):成员变量1(形参1),成员变量2(形参2){…}
class A{
A(intdata) : m_data(data){
//m_data=data;
}
int m_data;
}
意思相当于下例
int a;
a=2;
等价于int a=2;
举例:
1 #include<iostream>
2using namespace std;
3class student{
4public:
5 /*
6 //先定义成员变量,再在函数体中赋初值
7 student(const string&name,int age){
8 m_name=name;
9 m_age=age;
10 }*/
11 //定义成员变量同时初始化,提高一点效率
12 student(conststring& name,int age):
13 m_name(name),m_age(age){}
14 void who(void){
15 cout<<m_name<<','<<m_age<<endl;
16 }
17private:
18 string m_name;
19 int m_age;
20};
21int main(){
22 student s("张飞",25);
23 s.who();
24 return 0;
25 }
2) 必须要显式使用初始化表的地方:
a:如果有类类型的成员变量,该类又没有无参构造函数,则必须通过初始化表初始化该成员子对象
举例:
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(int data){
6 cout<<"A::A(int)"<<endl;
7 m_data=data;
8 }
9 int m_data;
10};
11class B{
12public:
13 /*通过初始化表制定成员子对象m_a的构造实参*/
14 B(void):m_a(100),m_i(200){
15 cout<<"B::B(void)"<<endl;
16 }
17 Am_a;//类类型成员变量
18 int m_i;
19};
20int main(void){
21 Bb;
22 cout<<b.m_a.m_data<<endl;//100
23 cout<<b.m_i<<endl;//200
24 return 0;
25 }
tarena@tarena-virtual-machine:~/day39$./a.out
A::A(int)
B::B(void)
100
200
b:类中包含“const”或“引用&”成员变量,必须用初始化表进行初始化
int a=100;
const int i; error
i=100; error
int& r=a; 引用的同时就必须初始化
举例:
1#include<iostream>
2using namespace std;
3int a=100;
4class A{
5public:
6 /* A(void){
7 m_r=a;
8 m_c=200;
9 }
10 int &m_r;
11 const int m_c; 错误*/
12 A(void):m_r(a),m_c(200){
13 }
14 int& m_r; // 初始化表初始化的作用:int& m_r=a
15 const int m_c; // const int m_c=200;
16};
17int main(void){
18 Aa;
19 cout<<a.m_r<<','<<a.m_c<<endl;
20 return 0;
21 }
3) 成员变量的初始化顺序由声明顺序决定,而与初始化表的顺序无关(笔试题)
1#include<iostream>
2#include<cstring>
3using namespace std;
4class dummy{
5public:
6 dummy(const char* psz):
7//m_str(psz),m_len(m_str.length()){}不能用一个成员变量去定义另一个成员变量
8 m_str(psz?psz : ""),
9 m_len(strlen(psz?psz : "")){}
10 //成员变量声明顺序决定初始化的顺序
11 //先声明应该先初始化
12 string m_str;
13 size_t m_len;
14};
15int main(){
16 dummy d("hello world");
17 cout<<d.m_str<<','<<d.m_len<<endl;
18 dummy e(NULL);
19 cout<<e.m_str<<','<<e.m_len<<endl;
20 return 0;
21 }
tarena@tarena-virtual-machine:~/day39$./a.out
hello world,11
,0
练习:修改时钟类,使用初始化表,
如果使用系统时间构造对象,表示为时钟功能
如果使用无参构造,在初始化表,把时间初始化为0,表现为计时器的功能
1#include<iostream>
2#include<cstdio>
3using namespace std;
4class Clock{
5public:
6 Clock(bool timer=false):
7 m_hour(0),m_min(0),m_sec(0){
8 if(timer){
9 time_t t=time(NULL);
10 tm* local=localtime(&t);
11 m_hour=local->tm_hour;
12 m_min=local->tm_min;
13 m_sec=local->tm_sec;
14 }
15 }
16 void run(void){
17 while(1){
18 show();
19 tick();
20 }
21 }
22private:
23 void show(void){
24 printf("\r%02d:%02d:%02d",m_hour,m_min,m_sec);
25 fflush(stdout);
26 }
27 void tick(void){
28 sleep(1);
29 if(++m_sec==60){
30 m_sec=0;
31 if(++m_min==60){
32 m_min=0;
33 if(++m_hour==24){
34 m_hour=0;
35 }
36 }
37
38 }
39 }
40private:
41 int m_hour;
42 int m_min;
43 int m_sec;
44};
45int main(void){
46 //Clock clock(time(NULL)); //时钟
47 Clock clock;//计时器
48 clock.run();
49 return 0;
50 }
练习:
实现一个Dog类,增加构造函数,要求使用初始化表,用有参的的方式实例化一个dog对象。
属性:
行为: