C++构造函数

#include <iostream>
#include <string>
using namespace std;

class Student {
    /*
    构造函数语法:类名(参数表){   } 
    注意:函数名与类名相同,没有返回值
    构造函数在创建对象时,会自动执行,主要用来初始化
    */
public:
    Student(const string& name1,int age1,int no1) {
        //注意:公有
        name = name1;
        age = age1;
        no = no1;
    }
private:
    string name;
public:
    int age;
    int no;
    
    void who(void) {
        cout << "我叫" << name << endl;
        cout << "今年" << age << "" << endl;
        cout << "学号是:" << no << endl;
    }
};

int main()
{
    Student s("张三", 25, 10011);  //创建对象并初始化
    s.who();
    return 0;
}

构造函数可以重载,可以带缺省参数

 

缺省构造函数:如果类中没有定义构造函数,编译器会提供一个缺省构造函数

对于基本类型成员变量不做初始化

对于类 类型的成员变量,将会自动调用相应的无参构造函数来初始化 

如果类中有构造函数,那么编译器就不会提供缺省的无参构造函数了

 

类型转换构造函数:

class A {
public:
    A() {
        m_i = 0;
    }
    A(int i) {  //类型转换构造函数,说明:可以用=方式在类外对私有成员进行赋值
        //必须是单参构造函数
        m_i = i;
    }

    void print() {
        cout << m_i << endl;
    }
private:
    int m_i;
};

int main()
{
    A a;
    a.print();
    a = 100;  //先把100隐试转换为A类型,保存在一个临时变量,通过临时变量再对m_i赋值;=后面的100可以看做实参;再次调用某个构造函数
    a.print();

}

 

class A {
public:
    A() {
        m_i = 0;
    }
    explicit A(int i) {  //类型转换构造函数
        //必须是单参构造函数
        //explicit修饰符:不能隐试转换,必须显示转换
        m_i = i;
    }

    void print() {
        cout << m_i << endl;
    }
private:
    int m_i;
};

int main()
{
    A a;
    a.print();
    a = (A)100;  //由于explicit,先把100显示转换为A类型,保存在一个临时变量,通过临时变量再对m_i赋值
    a.print();

}

 

 

 

拷贝构造函数(复制构造函数):

用一个已经存在的对象作为同类对象的构造实参,创建新的副本对象时,会调用该类拷贝构造函数

格式:

类名(const 类名&){

...

}

 

 

class A {
public:
    A(int i=0,int ii=0) {
        m_i = i;
        m_ii = ii;
    }
    A(const A& d) {  //拷贝构造函数
        //d 代表传过来的对象实参【--对象的引用】
        cout << "执行了拷贝构造函数" << endl;
        //注意:类中所有成员都要赋值
        m_i = d.m_i;
        m_ii = d.m_ii;
    }
    
    void print() {
        cout << m_i << endl;
        cout << m_ii << endl;
    }
private:
    int m_i;
    int m_ii;
};

int main()
{
    A a(123,789);
    a.print();
    A b(a); //实参是同类对象--调用拷贝构造函数
    b.print();
    A c = a; //与上面相同
    c.print();
    A e = A(a);//与上面相同
    e.print();

}

如果类内有拷贝构造函数,就执行自己的拷贝构造函数;如果类内没有,编译器会自动产生一个拷贝构造函数,如下实例

#include <iostream>
class A {
public:
    A(int i = 0, int ii = 0) {
        m_i = i;
        m_ii = ii;
    }
    
    void print() {
        std::cout << m_i << std::endl;
        std::cout << m_ii << std::endl;
    }
private:
    int m_i;
    int m_ii;
};

int main()
{
    A a(123, 789);
    a.print();
    A b(a); //实参是同类对象--调用拷贝构造函数
    b.print();
    A c = a; //与上面相同
    c.print();
    A e = A(a);//与上面相同
    e.print();

}

 

 

 

 

初始化列表:

普通构造函数:

    A(int i=0,int ii=0) {
        m_i = i;
        m_ii = ii;
    }

初始化列表的构造函数:

A(int i=0,int ii=0):m_i(i),m_ii(ii) {
       }
//
m_i用i来初始化,m_ii用ii来初始化

区别:

普通初始化:先给变量划出空间,再赋值

初始化列表:申请空间和赋值同时进行 

 

 

 

以下场景必须使用初始化列表:

场景一:如果有类对象成员,并希望以有参方式对其进行初始化,则必须使用初始化列表显示指明成员子对象需要的构造函数 

class A {
public:
    A(int i,int ii=0) {
        m_i = i;
        m_ii = ii;
        cout << "A构造函数" << endl;
    }
    
    void print() {
        cout << m_i << endl;
        cout << m_ii << endl;
    }
private:
    int m_i;
    int m_ii;
};

class B {
public:
    B():m_a(135) {
        //因为m_a是类对象成员,所以必须用初始化列表
        //说明:在创建m_a对象时,用135来初始化,135赋值给i
        //显示指明成员子对象的初始化方式
        //在创建B的对象时,先执行A的初始化函数,再执行B的初始化函数
        cout << "B的无参构造" << endl;
    }
private:
    A m_a;  //类对象
    };

int main()
{
    
    B b;
}

 

场景二:对const成员和引用成员的初始化:

C++11可以直接赋值

class A {
public:
    A():m_i(100),m_ii(g) {
        //m_i和m_ii,是const和引用,只能用初始化列表
        cout << "A构造函数" << endl;
    }
    
 
private:
    const int m_i;
    int& m_ii;
};



int main()
{
    A a;
    
}

 

 初始化顺序:

class A {
public:
    A(const char* str1):str(str1),len(str.size()) {
        cout << "A构造函数" << endl;
    }
    int len;
    const string str;
    //初始化顺序一般是根据声明顺序
    };

int main()
{
    A a("liming");
    cout << a.str << endl;
    cout << a.len << endl; //由于在求长度时,str还没有被初始化,所以返回值是乱值
    
}

 

拷贝构造在拷贝指针变量时要注意的问题: 

#include <iostream>
using namespace std;

class A {
public:
    A(int i = 0) {
        m_pi = new int(i);
    }
    void print(void)const {
        cout << m_pi << endl;  //打印指针地址

    }
    ~A(void) {   //析构函数--对象销毁时自动调用
        cout << "析构函数" << endl;
        delete m_pi;
    }
private:
    int* m_pi;
};

int main()
{
    A a(132);
    a.print();
    A b = a;
    b.print();
//两个地址是相同,系统默认的拷贝只拷贝变量本身(浅拷贝,对于指针变量只是复制地址),两个指针变量指向同一个数据
    //在执行析构函数时会出错:两个变量的地址是一样的,销毁第一次地址正常,销毁第二次地址就报错--因为这个地址变量已经不存在了
    //system("pause"); //暂停函数
     

   return 0;
}

解决方法:利用深拷贝:拷贝指针指向的数据,而不是拷贝指针变量

此时需要自己写拷贝函数了,不能用编译器提供的拷贝函数了

#include <iostream>
using namespace std;

class A {
public:
    class A(const A& that) {  //深拷贝构造函数
        m_pi = new int;  //先给指针变量分配内存
        //这个变量中的地址与传过来的就不一样了
        *m_pi = *that.m_pi; //拷贝指针指向的数据
    }
    A(int i = 0) {
        m_pi = new int(i);
    }
    void print(void)const {
        cout << m_pi << endl; 

    }
    ~A(void) {   
        cout << "析构函数" << endl;
        delete m_pi;
    }
private:
    int* m_pi;
};

int main()
{
    A a(132);
    a.print();
    A b = a;
    b.print();

    //system("pause"); 
     

   return 0;
}

 

拷贝构造与拷贝赋值: 

#include <iostream>
using namespace std;

class A {
public:
    A& operator=(const A& that) {  //拷贝赋值函数
        cout << "拷贝赋值函数" << endl;
        if (&that != this) {   //防止自赋值
            *m_pi = *that.m_pi;  //拷贝指针所指的数据
            }
        return *this;
    }

    class A(const A& that) {  
        m_pi = new int;  
        *m_pi = *that.m_pi; 
    }
    A(int i = 0) {
        m_pi = new int(i);
    }
    void print(void)const {
        cout << m_pi << endl; 

    }
    ~A(void) {   
        cout << "析构函数" << endl;
        delete m_pi;
    }
private:
    int* m_pi;
};

int main()
{
    A a(132);
    a.print();
    A b = a;  //这是拷贝构造
    b.print();

    A c(0);  //先创建对象
    c = a; //这是拷贝赋值
    //编译器默认浅拷贝
    //需要自定义拷贝赋值--深拷贝
    

    //system("pause"); 
     

   return 0;
}

全部默认参数或无参构造函数创建对象时注意的问题 

#include <iostream>
class A {
public:
    A(int i=10){
        m_pi = new int(i);
        ii = i;
        std::cout << i << std::endl;
    }
    void print(void) {
        std::cout << ii  << std::endl;
    }
    
private:
    int* m_pi;
    int ii;
};

int main()
{
    A a(100);
    A b();//注意:这个是不会创建对象的;编译器把这句当作函数的声明
    A c;  //这个会创建对象的
       
    
}

 

 

 

 

 

 

 

posted @ 2020-08-11 13:18  天子骄龙  阅读(240)  评论(0编辑  收藏  举报