{C++系列} C++基础 day05 初始化表,this指针与常函数,析构函数,对象创建和销毁过程,浅拷贝和深拷贝
in C/C++ with 0 comment

{C++系列} C++基础 day05 初始化表,this指针与常函数,析构函数,对象创建和销毁过程,浅拷贝和深拷贝

in C/C++ with 0 comment

复习:

1 类的定义和实例化

* 1)class/struct
* 2)public/private
* 3)构造函数

--》对象创建自动被调用    
--》负责初始化对象(成员变量)
--》不能显式直接调用

4)多文件编程

.h :类的声明
.cpp :类的实现

5)对象的创建和销毁

类名 对象(构造实参);
类名 对象 = 类名(构造实参);

类名* 对象指针 = new 类名(构造实参);
detele 对象指针;

2 #构造函数和初始化表

`1)`构造函数可以重载,可以带有缺省参数
`2)`缺省构造函数
`3)`类型转换构造函数 explicit
`4)`拷贝构造函数
class 类名{
    类名(const 类名& that){}
};

====================

十四# 构造函数和初始化表

5# 初始化表

1)#创建对象时也可以通过初始化表指明成员变量的初始化方式。

class 类名{
     类名(形参表):成员变量1(初值),...{
         //函数体
     }
 };

#举例

成员变量初始化表举例

#include <iostream>
using namespace std;

class Student{
public:
    //先把成员变量定义出来,再赋初值
    /*Student(const string& name,int age,int no){
        m_name = name;
        m_age = age;
        m_no = no;
    }*/
    /*使用初始化表:定义同时初始化*/
    Student(const string& name,int age,int no)
        :m_name(name),m_age(age),m_no(no){}

    void who(void){
        cout << "我叫" << m_name << ",今年" <<
            m_age << "岁,学号:" << m_no << endl;
    }
private:
    string m_name;
    int m_age;
    int m_no;
};
int main(void)
{
    Student s("林黛玉",28,10011);
    s.who();
    return 0;
}

成员子对象初始化表方式

#include <iostream>
using namespace std;

class A{
public:
    A(int data)/*:m_data(data)*/{
        m_data = data;    
        cout << "A的构造函数" << endl;
    }
    int m_data;
};
class B{
public:
    //“:m_a(123)”指明成员子对象m_a的初始化方式,
    //其中括号里面123等于是m_a的构造实参
    B(void):m_a(123){
        cout << "B的构造函数" << endl;
    }
    A m_a;
};
int main(void)
{
    B b;
    cout << b.m_a.m_data << endl;
    return 0;
}

2)#必须要显式使用初始化表的场景:

#练习:

使用初始化表为电子时钟类增加计时器的功能,如果使用日历时间创建对象,则表现为时钟功能;如果以无参方式创建对象,通过初始化表,将时间初始化00:00:00,则表现为计时器功能。

#include <iostream>
#include <cstdio>
#include <ctime>
#include <unistd.h>
using namespace std;

class Clock{
public:
    Clock(time_t t){
        tm* local = localtime(&t);
        m_hour = local->tm_hour;
        m_min = local->tm_min;
        m_sec = local->tm_sec;
    }
    Clock(void):m_hour(0),m_min(0),m_sec(0){}

    void run(void){
        while(1){
            show();//打印时间
            tick();//计时+1秒
            sleep(1);
        }
    }
private:
    void show(void){
        printf("\r%02d:%02d:%02d",
                m_hour,m_min,m_sec);
        //刷新输出缓冲区
        fflush(stdout);
    }
    void tick(void){
        if(60 == ++m_sec){
            m_sec = 0;
            if(60 == ++m_min){
                m_min = 0;
                if(24 == ++m_hour)
                    m_hour = 0;
            }
        }
    }
private:
    int m_hour;
    int m_min;
    int m_sec;
};
int main(void)
{
    //Clock c(time(NULL));
    Clock c;
    c.run();
    return 0;
}

注:成员变量初始化顺序由声明顺序决定,而与初始化表的顺序无关,所以建议不要使用一个成员变量去初始化其它成员变量。

十五 #this指针与常函数

//笔试题:什么是this指针,有什么作用?

1# this指针
1)#类中的函数都有一个隐藏的参数,类型是当前类类型指针,名字为this。

2)#需要显式this指针的场景

#举例

使用this指针举例

#include <iostream>
using namespace std;

class User{
public:
    User(const string& name,int age)
        :m_name(name),m_age(age){
        cout << "this=" << this << endl;    
    }
    void print(void){
        cout << m_name << "," << m_age << endl;
        cout << this->m_name << "," << 
            this->m_age << endl;
    }
    /*print编译之后会变成类似如下代码
     *void print(User* this){
     *  cout << this->m_name << "," << 
     *      this->m_age << endl;
     *}
     * */
private:
    string m_name;
    int m_age;
};
int main(void)
{
    User u1("张飞",28);
    User u2("赵云",25);
    u1.print();//u1.print(&u1);
    u2.print();
    cout << "&u1=" << &u1 << endl;
    cout << "&u2=" << &u2 << endl;
    return 0;
}

this指针区分作用域举例

#include <iostream>
using namespace std;

class User{
public:
    //参数名和成员变量名字一样,通过this区分
    User(const string& m_name,int m_age){
        this->m_name = m_name;
        this->m_age = m_age;
    }
    void print(void){
        cout << m_name << "," << m_age << endl;
    }
private:
    string m_name;
    int m_age;
};
int main(void)
{
    User u1("张飞",28);
    User u2("赵云",25);
    u1.print();//u1.print(&u1);
    u2.print();
    return 0;
}

this 指针调用自身对象,返回自引用举例

#include <iostream>
using namespace std;

class Counter{
public:
    Counter(int data = 0):m_data(data){}
    Counter& add(void){
        ++m_data;//++(this->m_data)
        //this指向调用对象
        //*this就是调用对象自身
        return *this;//返回自引用
    }
    void destroy(void){
        cout << "this=" << this << endl;
        delete this;//自销毁
    }
    int m_data;
};
int main(void)
{
    Counter cn;
    cn.add().add().add();
    cout << cn.m_data << endl;//3
    Counter* pcn = new Counter;
    //...
    //delete pcn;
    cout << "pcn=" << pcn << endl;
    pcn->destroy();
    
    return 0;
}

2# 常成员函数(常函数)

1)#在一个普通的成员函数后面加上const修饰,这个成员就称为常函数。
返回类型 函数名(形参表) const {函数体}
2)#常函数中的this指针是一个常指针,不能在常函数中修改成员变量的值。

注:被mutable关键字修饰的成员变量可以在常函数中被修改。

3)#非 常对象既可以调用常函数也可以调用非 常函数,而常对象只能调用常函数,不能调用非 常函数。
4)#函数名和形参表相同的成员函数,其常版本和非常版本可以构成重载关系,常对象调用常版本,非常对象调用非常版本。

## #举例

常成员函数举例

#include <iostream>
using namespace std;

class A{
public:
    A(int data = 0) : m_data(data){}
    void show(void) const {
        cout << m_data/*++*/ << endl;        
    }
    /*void show(const A* this)
     *  cout << this->m_data++ << endl;
     * */
private:
    /*mutable*/ int m_data;
};
int main(void)
{
    A a(123);
    a.show();
    a.show();
    return 0;
}

常函数调用举例

#include <iostream>
using namespace std;

class A{
public:
    //func1(const A* this)
    void func1(void)const{
        cout << "常函数" << endl;
    }
    //func2(A* this)
    void func2(void){
        cout << "非 常函数" << endl;
    }
};
int main(void)
{
    A a;
    //a.func1(&a)
    a.func1();
    a.func2();
    const A a2 = a;
    //a2.func1(&a2),const A*
    a2.func1();
    //常对象,不能调用非 常函数
    //a2.func2();//error

    const A* pa = &a;//pa:常指针
    pa->func1();
    //pa->func2();//error

    const A& ra = a;//ra:常引用
    ra.func1();
    //ra.func2();//error

    return 0;
}

十六# 析构函数(Destructor)
1# 语法:

class 类名{
    //析构函数
    ~类名(void){
        //清理对象在创建时所分配的动态资源
    }
};

1)#函数名必须是“~类名”
2)#没有返回类型,也没有参数
3)#不能被重载,即一个类只能有一个析构函数

2# 当对象被销毁时,该对象析构函数被自动执行

3# 如果一个类没有定义析构函数,那么编译器会为该类提供一个缺省析构函数

1)对基本类型的成员变量,什么也不做
2)对类 类型的成员变量,调用相应类的析构函数

4 #对象的创建和销毁过程
1)#创建

2)#销毁

#举例

析构函数举例

#include <iostream>
using namespace std;
class A{
public:
    ~A(void){
        cout << "A::~A(void)" << endl;
    }
};
int main(void)
{
    do{
        A a;
        cout << "test1" << endl;
        A* pa = new A;
        delete pa;//A::~A(void)
        cout << "test3" << endl;
    }while(0);//A::~A(void)

    cout << "test2" << endl;
    return 0;    
}

类成员子对象调用析构函数举例

#include <iostream>
using namespace std;

class A{
public:
    A(void){
        cout << "A::构造,分配了动态资源..."
            << endl;
    }
    ~A(void){
        cout << "A::析构,释放了动态资源..."
            << endl;
    }
};
class B{
public:
    B(void){
        cout << "B::B(void)" << endl;
    }
    ~B(void){
        cout << "B::~B(void)" << endl;
    }
    A m_a;
};
int main(void)
{
    B b;
    return 0;
}

=======================

十七 #拷贝构造和拷贝赋值

1 浅拷贝和深拷贝

1)#如果一个类中包含了指针形式的成员变量,缺省的拷贝构造只是复制了指针变量的自身,而没有复制指针所指向的内容,这种拷贝方式称为浅拷贝

2)#浅拷贝会导致不同对象之间的数据共享,如果指针的数据在堆区,会在析构时引发“double free”的异常。

3)#为了避免浅拷贝的问题,就需要自己定义一个支持复制指针内容拷贝构造函数,即深拷贝。

#举例

浅拷贝和深拷贝举例

#include <iostream>
using namespace std;

class Integer{
public:
    Integer(int data = 0):m_data(new int(data)){
        //m_data = new int(data);
    }
    ~Integer(void){
        delete m_data;
        m_data = NULL;
        cout << "析构函数" << endl;
    }
    //缺省的拷贝构造函数(浅拷贝)
    //Integer i2(i);
    //i2.m_data = i.m_data;
    /*Integer(const Integer& that)
        :m_data(that.m_data){
        //m_data = that.m_data;
    }*/
    //自定义深拷贝
    //Integer i2(i);
    //i2.m_data = new int;
    //*i2.m_data = *i.m_data;
    Integer(const Integer& that)
        :m_data(new int(*that.m_data)){
        //m_data = new int(*that.m_data);
    }
    void print(void){
        cout << *m_data << endl;
    }
private:
    int* m_data;
};
int main(void)
{
    Integer i(100);
    i.print();
    Integer i2(i);
    i2.print();
    return 0;
}
Responses