{C++系列} C++基础 day04 类的定义和实例化,构造函数,多文件编程,对象创建和销毁,类型转换构造函数,拷贝构造函数,
in C/C++ with 0 comment

{C++系列} C++基础 day04 类的定义和实例化,构造函数,多文件编程,对象创建和销毁,类型转换构造函数,拷贝构造函数,

in C/C++ with 0 comment

#复习:

1# 引用

2# 类型转换

4# 类和对象

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

十三 #类的定义和实例化

1# 类的语法形式

class/struct 类名:继承方式 基类,...{
访问控制限定符:
    类名(形参表):初始化表{函数体}//构造函数
    ~类名(void){函数体}//析构函数
    返回类型 函数名(形参表){函数体}//成员函数
    数据类型 变量名;//成员变量
};  

2# 访问控制限定符

eg:
struct/class A{
public:
  aaa;//公有成员
private:
  bbb;//私有成员
public:
  ccc;//公有成员
};

3# 构造函数(Constructor)

class 类名{
    类名(形参表){...}
};

#举例

构造函数举例

#include <iostream>
using namespace std;

class Student{
public:
    Student(const string& name,int age,int no){
        cout << "构造函数" << endl;
        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)
{
    //创建对象,会自动调用构造函数
    //1)为s对象分配内存
    //2)自动调用构造函数,完成对s的初始化
    //注:(...)指定调用构造函数时需要的实参
    Student s("张飞",28,10086);
    s.who();

    //构造函数不能像普通成员一样直接调用
    //s.Student("张三",18,10011);
    return 0;
}

#练习:

实现一个电子时钟类,通过构造参数接收当前的系统时间,以秒为单位运行。

演示代码

class Clock{
public:
    Clock(time_t t){//时间初始化
       //将日历时间准换为当前系统时间
        tm* local = localtime(&t);//<ctime>
        m_hour = local->tm_hour;
        m_min = local->tm_min;
        m_sec = local->tm_sec;
    }
    void run(void){
        while(1){
            //打印当前时间,printf <cstdio>
            //计时加1秒
            sleep(1);//<unistd.h>
        }
    }
private:
    int m_hour;
    int m_min;
    int m_sec;
};
int main(void){
    Clock c(time(NULL));
    c.run();
}

实用代码

#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;
    }
    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));
    c.run();
    return 0;
}

4# 多文件编程

5# 对象的创建和销毁
1)#在栈中创建单个对象
类名 对象(构造实参表);
类名 对象 = 类名(构造实参表);

注:两种写法完全等价

2)#在栈中创建多个对象(对象数组)
类名 对象数组[元素个数] = {类名(构造实参表),...};

3)#在堆区创建单个对象
创建:
类名* 对象指针 = new 类名(构造实参表);
注:new对象时,除了分配内存还会调用构造函数
销毁:

delete 对象指针;

4)#在堆区创建多个对象
创建:

类名* 对象指针 = new 类名[元素个数]{类名(构造实参表),..};

销毁:

delete 对象指针;

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

eg:
class A{
public:
    A(void){m_i = 100;}
    int m_i;
};
class B{
public:
   //B(void){}//编译器提供缺省无参构造函数
    int m_j;//基本类型成员变量
    A m_a;//类 类型的成员变量
};
int main(void){
    B b;
    cout << b.m_j << endl;//未初始化值
    cout << b.m_a.m_i << endl;//100
}

2)#如果定义了构造函数,无论是否有参数,那么编译器都不会再提供缺省(无参)构造函数。

3 #类型转换构造函数(单参构造函数)

class 目标类型{
     目标类型(源类型 str){...}
};

可以实现从源类型到目标类型的转换。
注:使用explicit关键字修饰该构造函数,那可以强制这种转换必须显式进行。

#举例

类型转换构造函数举例

#include <iostream>
using namespace std;

class Integer{
public:
    Integer(void){
        cout << "Integer::Integer()" << endl;
        m_data = 0;    
    }
    //类型转换构造函数
    //explicit要求通过构造函数实现的类型转换必须
    //显式的完成。
    explicit Integer(int data){
        cout << "Integer::Integer(int)" << endl;
        m_data = data;
    }
    void print(void){
        cout << m_data << endl;
    }
private:
    int m_data;
};
int main(void)
{
    Integer i;
    i.print();//0
    i = 100;//通过构造函数隐士转换
    i.print();//100

    //通过构造函数显式转换
    //i = (Integer)200;
    i = Integer(200);
    i.print();
    return 0;
}

4# 拷贝构造函数(复制构造函数)

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

class 类名{
    类名(const 类名& that){..}
};
eg;
class A{
public:
   A(const A& that){...}
};
A a1;
A a2(a1);//调用拷贝构造函数
A a2 = a1;//和上面等价

2)#如果一个类没有定义拷贝构造函数,那么编译器会为该类提供一个缺省的拷贝构造函数:

--》对基本类型的成员变量,按字节复制。
--》对类 类型的成员变量,调用相应类的拷贝构造函数,初始化该成员子对象。

3)#拷贝构造函数的调用时机:

    --》用已定义的对象作为同类型对象的构造实参
    --》以对象形式向函数传递参数
class A{...};
void foo(A a){}
int main(void){
    A a2;
    foo(a2);//拷贝构造
}

--》从函数中返回对象(有时可能被编译器优化掉)

class A{...};
A foo(void){
    A a;
    return a;//A 临时对象 = a;//拷贝构造
}
int main(void){
    A a2 = foo();//拷贝构造
}

#举例

拷贝构造函数举例

#include <iostream>
using namespace std;

class A{
public:
    A(int data = 0){
        cout << "A的普通构造函数" << endl;
        m_data = data;
    }
    A(const A& that){
        cout << "A的拷贝构造函数" << endl;
        m_data = that.m_data;
    }
    int m_data;
};
class B{
public:
    A m_a;//类类型成员变量(成员子对象)
};
int main(void)
{
    B b;
    b.m_a.m_data = 123;
    cout << b.m_a.m_data << endl;//123
    /*B类中没有定义拷贝构造函数,编译器会提供一
     * 个缺省拷贝构造函数,在拷贝构造b2对象时,
     * 会先将b2里面所包含的成员子对象m_a构造出来, 
     * 这时会自动调用A的拷贝构造函数,初始化m_a*/
    B b2(b);//拷贝构造
    cout << b2.m_a.m_data << endl;//123
    return 0;
}

#例子

以下代码共调用几次构造函数,为什么

#include <iostream>
using namespace std;

class A{
public:
    A(void){
        cout << "A::A(void)" << endl;
    }
    A(const A& that){
        cout << "A::A(const A&)" << endl;
    }
};
void foo(A a){}
A bar(void){
    A a;//A::A(void)
    cout << "bar::&a=" << &a << endl;
    return a;//A temp = a;-->A::A(const A&)
}
int main(void)
{
    A a1;//A::A(void)
    A a2 = a1;//A::A(const A&)
    foo(a1);//A::A(const A&)
    A a3 = bar();//A::A(const A&)
    cout << "main::&a3=" << &a3 << endl;
    return 0;
}

共调用六次构造函数,当A a3=bar();调用时,在函数中定义A a对象,无参构造函数被调用,return a时候,把对象只拷贝给返回值的临时变量,拷贝构造函数被调用,然后对象值传递给a3时拷贝构造函数再次被调用。

Responses