C++类继承

C++类继承

1.is-a继承

1.1公有继承

特性:

//保持基类的访问权限不变,基类数据成员只能通过基类的方法访问

//派生类需要自己的构造函数,创建对象时,需要使用成员初始化列表,将基类成员一起初始化

//派生类可以重载基类成员函数,也可以添加额外的方法和数据成员

//创建派生类对象时,基类对象将被先行创建,即先调用基类的构造函数

//如果不调用基类构造函数,直接创建派生类对象时,将使用默认的基类构造函数

//实例
class Person{
private:
    char m_name[10];
    char m_sex[10];
public:
    Person(const char *name="yang",const char *sex="male"){
		strcpy(m_name,name);
        strcpy(m_sex,sex);
        cout<<"我是基类构造函数,我被优先调用了\n";
    }
    void show() const{
		cout<<m_name;
		cout<<"\n";
		cout<<m_sex;
        cout<<"\n";
	}
};
class Student:public Person{
private:
    char m_xuehao[10];
public:
    Student(const char *name="xu",const char *sex="male",const char *xuehao="1510370221")
        :Person(name,sex){//使用成员初始化列表,如果省略,则默认调用Person()构造函数
			strcpy(m_xuehao,xuehao);
        }
    void display() const{
        show();
        cout<<"\n";
        cout<<m_xuehao;
    }
};  //注意赋值一个常量字符串给一个char *时,必须加上const,由于字符串为一个常量,不能被修改,会发出警告!!!!
int main(){
	Person p;
	p.show();
    
    Student s;
    s.display();
}//打印:  
	/*  我是基类构造函数,我被优先调用了
		yang
    	male
    	我是基类构造函数,我被优先调用了
		xu
		male
		1510370221
		*/

//此外如果构造函数new一块内存,还需要重定义新的复制构造函数

//派生类指针不能指向和引用基类,即不能讲基类放大; 但是可以使用基类指针指向和引用派生类,可以放窄
Person p;
Student *s=p;//error

Student s;
Person *p=s;//允许

//派生类对象可以赋给基类对象,不能反转 
Student s;
Person p=s;//语义:Student is a person!but,person not a student.

1.2私有继承

用的少,不是重点…

1.3保护继承

主要用于继承中派生类可以直接访问基类的protected成员

2.has-a关系

//方法一:在类使用其他类对象
class Student{
private:
    string m_name;//使用string类对象
public:
    Student(const string name){ ... }
    
}

//方法二:使用私有继承
class Student:private string{//私有继承string对象
private:
    char m_name[10];
    
public:
    Student(const char* name="yang")
        :string(name){ ... }
}

3.多态公有继承

3.1特性:

  • 能够在派生类中重定义基类的方法,注意不是重载,是重定义,原型不一定相同,函数体不同,将隐藏基类方法;;;重载是原型不同,函数体必须相同;;;如果基类虚函数被重载,则需在派生类重定义所有版本

  • 基类使用虚方法,在基类和派生类中不同实现

  • 编译器对非虚方法使用静态联编,是哪个数据类型调用哪个方法;使用虚方法,将动态联编,根据指针和引用对象来调用相应的方法

  • 静态联编:执行效率更高,如果类不用做基类,就不使用动态联编!!只有预期哪些将被重定义的方法声明为虚的!

//实例
class Person{
private:
    char m_name[10];
    char m_sex[10];
public:
    Person(const char *name="yang",const char *sex="male"){
		strcpy(m_name,name);
        strcpy(m_sex,sex);
        cout<<"我是基类构造函数,我被优先调用了\n";
    }
    virtual ~Person(){}//基类使用虚析构方法,保证释放派生对象时,按照正确的顺序调用析构函数
    
   	virtual void show() const{//基类使用虚方法,方便程序根据对象就能调用相应的方法
		cout<<m_name;
		cout<<"\n";
		cout<<m_sex;
        cout<<"\n";
	}
};
class Student:public Person{
private:
    char m_xuehao[10];
public:
    Student(const char *name="xu",const char *sex="male",const char *xuehao="1510370221")
        :Person(name,sex){
			strcpy(m_xuehao,xuehao);
        }
   virtual void display() const{//派生类加上virtual,方便识别,可以省略,最好加上
        Person::show();//显式调用基类的同名方法,作用解析符不能省略,否则将无限递归
        cout<<"\n";
        cout<<m_xuehao;
    }
}; 

int main(){
	Student s;
    Person *p=&s;
    p->show();//由于show声明为virtual,采用动态联编根据对象调用Student::show()
}

3.2虚函数实现原理

//使用一个指向虚函数表的指针vtbl

//派生类与基类共用一个指向虚函数表的指针,如果虚函数没有被重新定义,则此虚函数地
址不变,如    
果改变或者添加了新的虚函数,将保存为一个新的地址!!

//无论虚函数有多少个,类与实例对象都只保存一个vtbl,只是指向的虚函数表的大小不同而已!!

//可见使用虚函数,将增加以下时间和空间成本
	/*
		1.每个对象额外增加一个vtbl内存
		2.每个类将创建一个虚拟地址表数组
		3.每次函数调用,需要查找地址
	*/
//注意几点:构造函数不能为虚,析构函数必须为虚!否则只会释放基类的new部分内存,造成内存泄漏;
友元函数不能为虚!
Person *p=new Student;
...
delete p;//假设Student对象new一个内存,此时只会调用Person的析构函数,
但是Student内存没有释放,而使用虚析构函数,则直接调用Student的析构函数,从而释放全部的内存!!    

注:以上代码为了方便,直接定义与实现一起写了,这样会导致方法均是内联函数,非常浪费内存,所以还是将方法实现放在定义之外!!!

4.纯虚函数和抽象基类

//纯虚函数指在基类中没有实现代码,专门为了派生类继承重定义使用!!
virtual void show() const=0;//纯虚函数

//含有纯虚函数的类都可以叫做抽象类,专门用来被继承,重定义实现的,不能被创建实例!

//好处:方便将多个派生类使用基类指针数组进行统一管理

5.多继承、多重继承和虚拟继承

//多继承,比如班长多继承Leader,Student,
class Monitor:public Leader,public Student{
    ...
}

//多重继承,由于Leader,Student又继承与Person,导致Monitor存在Person的两份拷贝,非常浪费内存,完全没必要
class Monitor:public Leader,public Student{
    ...
}

//使用虚拟继承
class Leader:virtual public Person{
    ...
}
class Student:virtual public Person{
    ...
}
class Monitor:public Leader,public Student{
    ...
}//使用虚拟继承从而解决了拷贝多基类的问题,只会保留一份拷贝

6.模板类

+补坑…

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值