Day39、构造函数重载、缺省构造函数、拷贝构造函数、初始化表

本文详细讲解了C++中的构造函数重载、缺省构造函数、拷贝构造函数以及初始化表的使用。通过示例代码展示了如何定义和使用这些构造函数,以及它们在对象创建过程中的作用。此外,还提到了编译器提供的默认构造函数和拷贝构造函数的情况,以及何时需要自定义这些构造函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、            构造函数和初始化表

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对象。

属性:

行为:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值