构造函数、析构函数、继承

1.构造函数
通过结构体创建一个对象时,需要先声明结构体变量、再给结构体每个成员赋值(也就是所谓的初始化);
如果能够在声明变量时就直接给结构成员赋值会更加方便;构造函数就是为了达到这一目的;
 
构造函数:
    结构体中的Person函数就是构造函数
struct Person            
{            
    int age;        
    int level;        
    Person()        
    {        
        printf("Person对象创建了\n");    
    }  
    
    //构造函数      
    Person(int age,int level)        
    {        
        this->age = age;    
        this->level = level;    
    }  
    
    //成员函数      
    void Print()        
    {        
        printf("%d-%d\n",age,level);    
    }        
};            

 

构造函数的特点:
    1】与类同名            
    2】没有返回值            
    3】创建对象的时候执行            
    4】主要用于初始化            
    5】可以有多个(最好有一个无参的),称为重载 其他函数也可以重载            
    6】编译器不要求必须提供            
 
重载:
    当没有构造函数时,编译器默认提供一个无参数的构造函数;
    声明对象时编译器会调用构造函数,如果有继承关系,子类的构造函数会调用父类构造函数,以此类推直到调用最上级类的构造函数;
    如果添加了构造函数,将不再有默认的无参数构造函数,需要自己添加;
    可以有多个构造函数,每个构造函数参数不能相同,这种机制称为重载;
    也就是说函数名字可以一样,只要参数的类型或数量不一样就可以;
    注意重载与函数的返回类型是无关的,;
    成员函数也可以重载;重载的好处是可以少给函数起名字;
 
2.析构函数
构造函数时用来做初始化的;
析构函数是用来做收尾清理工作的;
 
一个带析构函数的结构:
#include "malloc.h"                        
struct Person            
{            
    int age;        
    int level;        
    char* arr;  
     
    //构造函数 
    Person()        
    {        
        printf("无参构造函数执行了...");    
    }        
    Person(int age,int level)        
    {        
        printf("有参构造函数执行了...");    
        this->age = age;    
        this->level = level;    
        arr = (char*)malloc(1024);    
    }    
 
    //析构函数   
    ~Person()        
    {        
        printf("析构函数执行了...");    
        free(arr);    
        arr = NULL;    
    } 
 
    //成员函数       
    void Print()        
    {        
        printf("%d-%d\n",age,level);    
    }        
};    

 

析构函数的特点:
    1】只能有一个析构函数,不能重载            
    2】不能带任何参数            
    3】不能带返回值            
    4】主要用于清理工作            
    5】编译器不要求必须提供     
 
有些结构创建的对象的成员在初始化时需要用malloc在堆中申请了内存;
malloc申请的内存在不再使用时需要主动调用free函数释放;
当对象销毁时,可以确定申请的堆内存不再使用,此时需要释内存;
可以将释放内存的free方法写在析构函数中;
析构函数在对象销毁时自动调用;
析构函数名字与类名一样,只是前面必须加~;
        
3.继承
比如说要创建很多个结构;例如Studen、Teacher;
如果这些结构中都有几个相同的成员;例如Student和Teacher中都有表示年龄和性别的成员;
这就意味着每次都要写重复的代码;
可以将多个结构共有的成员放在一个单独的结构中;
然后这些结构都继承这个单独的结构,即可达到简化代码的目的;
 
不使用继承时:
struct Person        
{        
    int age;    
    int sex;    
};        
struct Teacher        
{        
    int age;    
    int sex;    
    int level;    
    int classId;    
};        
struct Student        
{        
    int age;    
    int sex;    
    int code;    
    int score;    
};        
反汇编:
 
使用继承时:
struct Person        
{        
    int age;    
    int sex;    
};        
struct Teacher:Person        
{        
    int level;    
    int classId;    
};        
struct Student:Person        
{        
    int code;    
    int score;    
};        
反汇编分析:
    
可以看到,使用继承和不适用继承反汇编是完全一样的;
继承本质上就是代码的复制,也就是编译器替我们做了重复的工作;
 
总结:
    1】什么是继承?                
        继承就是数据的复制                
    2】为什么要用继承?                
        减少重复代码的编写                
    3】Person 称为父类或者基类    
    4】Teacher、Student称为子类或者派生类                
    5】t和s可以称为对象或者实例.                
    6】可以用父类指针指向子类的对象.                
 
关于父类指针指向子类对象:
    如图:一个Student对象,里面有4个成员;
    Student的指针,指向结构开始的地方,可以访问x、y、a、b;
    Person的指针也可以指向该结构,并且不需要转换;
    因为子类对象本质上就是先将父类成员复制一份,然后再添加自己特有的成员;
    也就是说子类对象前两个成员x和y合起来就是父类Person的结构;
    当Person类型的指针指向子类对象开始的地方时,访问x和y完全没问题;
    但在编译器看来Person类型的指针并不认识a和b无法直接访问子类对象的特有成员;
    
 
如果子类指针指向父类对象则是不可以的,需要强转;
因为子类指针表示的范围可能比父类大;
 
4.多层继承
struct X    
{    
    int a;
    int b;
};    
struct Y:X    
{    
    int c;
    int d;
};    
struct Z:Y    
{    
    int e;
    int f;
};    
 
可以用x的指针指向z的对象;
z的对象中包含x和y的成员;
 
如果有一种情况:子类某个成员和父类的成员名字一样时;
比如结构y中也有个成员叫a时;
此时结构z的成员数量并不会有变化;
但在使用时必须告诉编译器是哪个结构的a;例如这里用双冒号来指定是哪个范围的a;
这种重名的情况在反汇编时和不重名没有任何区别,只是需要告诉编译器谁是谁而已;
struct X        
{        
    int a;    
    int b;    
};        
struct Y:X        
{        
    int a;    
    int d;    
};        
struct Z:Y        
{        
    int e;    
    int f;    
};        
        
Z z;        
z.X::a = 1;        
z.b = 2;        
z.Y::a = 3;        
z.d = 4;        
z.e = 5;        
z.f = 6; 

 

5.多重继承
c++支持多继承;
这种继承比较复杂,编译器需要维护多个地址压力比较,不推荐使用;
struct X    
{    
    int a;
    int b;
};    
struct Y    
{    
    int c;
    int d;
};    
struct Z:X,Y    
{    
    int e;
    int f;
};    

 

6.练习
#include "MyClass.h"
#include <stdio.h>
#include <stdlib.h>
 
struct DateInfo{    //父类
    int year;
    int month;
    int day;
 
    DateInfo(int year, int month, int day){
        this->year = year;
        this->month = month;
        this->day = day;
    }
 
    DateInfo(){
        year = 1970;
        month = 1;
        day = 1;
    }
 
    void setDay(int day){
        this->day = day;
    }
    int getDay(){
        return day;
    }
 
    void setMonth(int month){
        this->month = month;
    }
    int getMonth(){
        return month;
    }
 
    void setYear(int year){
        this->year = year;
    }
    int getYear(){
        return year;
    }
 
};
 
struct TimeInfo:DateInfo{    //继承
    int hour;
    int minute;
    int second;
 
    TimeInfo(int hour, int minute, int second){
        this->hour = hour;
        this->minute = minute;
        this->second = second;
    }
    TimeInfo(){
        hour = minute = year = 0;
    }
 
    void setHour(int hour){
        this->hour = hour;
    }
    int getHour(){
        return hour;
    }
 
    void setMinute(int minute){
        this->minute = minute;
    }
    int getMinute(){
        return minute;
    }
 
    void setSecond(int second){
        this->second = second;
    }
    int getSecond(){
        return second;
    }
};
 
void testTime(){
    TimeInfo time(11,12,13);
    DateInfo* date = &time;    //父类指针指向子类对象
    printf("时间:%d/%d/%d\t%d:%d:%d\n",time.getYear(),time.getMonth(),time.getDay(),time.getHour(),time.getMinute(),time.getSecond());
    printf("父类指针指向子类对象:%d/%d/%d\n",date->year, date->month, date->day);
}
 
struct MyString{
    char* str;
 
    MyString(){    //无参构造
        str = (char*)malloc(1024);    
    }
 
    MyString(char* str){
        int i = 0;
        while(*(str+i)){    //直到字符串结束符号0
            i++;
        }
        this->str = (char*)malloc(i+1);
        for(int j =0;j<i+1;j++){
            *(this->str + j) = *(str + j);
        }
    }
 
    ~MyString(){
        printf("调用析构函数,释放内存空间\n");
        free(str);
    }
};
 
void testMyString(){
    MyString str("Hello World");
    printf("%s\n", str.str);
}
 
void main(){
    //testTime();
 
    testMyString();
 
    getchar();
}

 

 
posted @ 2019-11-27 10:09  L丶银甲闪闪  阅读(386)  评论(0编辑  收藏  举报