C++基础3:继承

1. 语法

  • 原则:is-a (什么是什么)
    父类/子类
    基类/派生类

  • 语法

class 派生类 : [访问限定符] 基类 {
  成员
}
如果不写继承访问限定符,默认是private

a.继承的时候一定是public

class EqualTriangle:public Triangle{   //a.继承的时候一定是public

b.构造函数的写法:父类构造i函数初始化列表;

EqualTriangle(int bottom, int side):Triangle(bottom,side,side){}  //b.构造函数的写法:父类构造i函数初始化列表;
  • 完整案例
#include <iostream>
using namespace std;
#include <cmath>
class Triangle{
int a,b,c;
public:
	Triangle(int a, int b, int c):a(a),b(b),c(c){}
	int  GetLength()const{
	
		return a + b + c;
	}
	int GetArea()const{
		float p = (a+b+c)/2.0;
		return sqrt(p*(p-a)*(p-b)*(p-c));
	
	}


};

class EqualTriangle:public Triangle{   //a.继承的时候一定是public
public:
	EqualTriangle(int bottom, int side):Triangle(bottom,side,side){}  //b.构造函数的写法:父类构造i函数初始化列表;

};


int main (){
	cout << sizeof(EqualTriangle) <<endl;
	EqualTriangle e(10,15);
	cout << e.GetLength() << endl;
	cout << e.GetArea() << endl;


}

c.子类不能访问父类的私有变量,子类想直接访问父的变量,但是为私有的情况;

int GetBottom()const{  //子类想直接访问父的变量,但是为私有的情况;
		return a;	
}

解决方法:

  1. protected:不允许外部对象访问,但是子类是可以的;
protected: //解决之道1:protected:不允许外部对象访问,但是子类是可以的;
  1. 成员函数的方案
int GetA()const{return a;}			//解决之道2:成员函数的方案
  • 完整案例
#include <iostream>
using namespace std;
#include <cmath>
class Triangle{
//private:  //a.子类不能访问父类的私有变量
protected: //解决方法1:protected:不允许外部对象访问,但是子类是可以的;
int a,b,c; 
public:
	Triangle(int a, int b, int c):a(a),b(b),c(c){}
	int  GetLength()const{
	
		return a + b + c;
	}
	int GetArea()const{
		float p = (a+b+c)/2.0;
		return sqrt(p*(p-a)*(p-b)*(p-c));
	
	}
	//int GetA()const{return a;}			//解决方法2:成员函数的方案
};

class EqualTriangle:public Triangle{
public:
	EqualTriangle(int bottom, int side):Triangle(bottom,side,side){}
	int GetBottom()const{  //子类想直接访问父的变量,但是为私有的情况;
		return a;	
	}
	int GetSide()const{
		return b;	
	}
};

int main (){
	cout << sizeof(EqualTriangle) <<endl;
	EqualTriangle e(10,15);
	cout << e.GetLength() << endl;
	cout << e.GetArea() << end;
}

2. 成员的访问权限

-publicprotectedprivate
类成员函数
友元函数
子类函数×
类对象××
子类继承了父类所有的成元变量和成员函数。与访问限定符无关。访问限定符只是限制了访问。
子类访问父类成员变量,把父类成员变量访问限制符,改为protected。
  • 案例
    a. 在类里面,可以访问到内部定义的任何成员
    b. 对象只能访问类定义的public成员
    c. __ func__ 函数返回函数名,左右为两个下划线
#include <iostream>
using namespace std;

class Simple{
public:
	void Test(){ // 在类里面,可以访问到内部定义的任何成员。
		cout << __func__ << endl; 
		Public();
		Protected();
		Private();
	}
	void Public(){
		cout << __func__ << endl;
	}
protected:
	void Protected(){
		cout << __func__ << endl;
	}
private:
	void Private(){
		cout << __func__ << endl;
	}
};

int main(){
	Simple t;
//	t.Test(); // 对象调用的类型里面的公有的成员函数,可以访问内部的所有成员(变量+函数)
	t.Public(); // 对象只能访问类定义的public成员
//	t.Protected();
//	t.Private();
}
子类继承了父类所有的成元变量和成员函数。与访问限定符无关。访问限定符只是限制了访问。
子类访问父类成员变量,把父类成员变量访问限制符,改为protected。
  • 继承访问权限变化

分为子类内部和子类对象两种访问方式。

  • 子类内部访问父类成员
-publicprotectedprivate
public 继承×
protected 继承×
private 继承×
子类内部访问父类成员,只能访问public和protected成员。
  • 子类对象访问父类成员
-publicprotectedprivate
public 继承××
protected 继承×××
private 继承×××
子类只有public继承父类的时候,才能访问父类的public成员,其他都不能访问。
通常子类使用public继承父类。
  • 子类对象访问父类成员访问限定符的变化
继承方式\父类成员publicprotectedprivate
public 继承publicprotected不可见
protected 继承protectedprotected不可见
private 继承privateprivate不可见

(public 继承)
a. public继承,父类public成员以public继承到子类,protected成员以protecte 继承到子类
public、protected、private继承时,子类里面可以访问除了private所有成员

class Drivce:public Simple{
public:
    void DrivceTest(){
        // public继承,父类public成员以public继承到子类,protected成员以protecte    d继承到子类
		//public、protected、private继承时,子类里面可以访问除了private所有成员
        Public();
        Protected();
       // Private();
    }
};

b.public继承时,子类对象只可以访问父类的public成员

d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
    // d.Protected();
    // d.Private();
  • 完整代码
#include <iostream>
using namespace std;

class Simple{
public:
    void Test(){ // 在类里面,可以访问到内部定义的任何成员。
        cout << __func__ << endl;
        Public();
        Protected();
        Private();
    }
    void Public(){
        cout << __func__ << endl;
    }
protected:
    void Protected(){
        cout << __func__ << endl;
    }
private:
    void Private(){
        cout << __func__ << endl;
    }
};
class Drivce:public Simple{
public:
    void DrivceTest(){
        // public继承,父类public成员以public继承到子类,protected成员以protected 继承到子类
	//public protected、private继承时,子类里面可以访问除了private所有成员
        Public();
        Protected();
       // Private();
    }
};

class SubDivce:public Drivce{
public:
     void SubDivceTest(){
        Public();
        Protected();
     }
};

int  main(){
    Simple t;
    Drivce d;
    d.DrivceTest();
    d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
    // d.Protected();
    // d.Private();
}

在这里插入图片描述
(protected 继承)
a. protected继承,父类public成员以protected继承到子类,protected成员以protected继承到子类

class Drivce:protected Simple{
public:
    void DrivceTest(){
        // public、protected、private继承时,子类里面可以访问除了private所有成员
    // protected继承,父类public成员以protected继承到子类,protected成员以protected继承到子类
        Public();
        Protected();
       // Private();
    }
};

b.protected、private继承时,子类对象不能访问父类的所有成员

d.DrivceTest();
   //  d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
    // d.Protected();
    // d.Private();
  • 完整案例
#include <iostream>
using namespace std;

class Simple{
public:
    void Test(){ // 在类里面,可以访问到内部定义的任何成员。
        cout << __func__ << endl;
        Public();
        Protected();
        Private();
    }
    void Public(){
        cout << __func__ << endl;
    }
protected:
    void Protected(){
        cout << __func__ << endl;
    }
private:
    void Private(){
        cout << __func__ << endl;
    }
};
class Drivce:protected Simple{
public:
    void DrivceTest(){
        // public、protected、private继承时,子类里面可以访问除了private所有成员
    // protected继承,父类public成员以protected继承到子类,protected成员以protected继承到子类
        Public();
        Protected();
       // Private();
    }
};

class SubDivce:public Drivce{
public:
     void SubDivceTest(){
        Public();
        Protected();
     }
};

int  main(){
    Simple t;
    Drivce d;
     d.DrivceTest();
   //  d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
    // d.Protected();
    // d.Private();
}

在这里插入图片描述
(private 继承)
a.public、protected、private继承时,子类里面可以访问除了private所有成员,
private继承 , 父类public成员以private 继承到子类,potected 成员以 private 继承到子类

class Drivce:private Simple{
public:

    void DrivceTest(){
        // public、protected、private继承时,子类里面可以访问除了private所有成员
    // private继承                    private                             private
        Public();
        Protected();
       // Private();
    }
};

b.protected、private继承时,子类对象不能访问父类的所有成员

d.DrivceTest();
    // d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
    // d.Protected();
  • 完整案例
#include <iostream>
using namespace std;

class Simple{
public:
    void Test(){ // 在类里面,可以访问到内部定义的任何成员。
        cout << __func__ << endl;
        Public();
        Protected();
        Private();
    }
    void Public(){
        cout << __func__ << endl;
    }
protected:
    void Protected(){
        cout << __func__ << endl;
    }
private:
    void Private(){
        cout << __func__ << endl;
    }
};
class Drivce:private Simple{
public:

    void DrivceTest(){
        // public、protected、private继承时,子类里面可以访问除了private所有成员
    // private继承                    private                             private
        Public();
        Protected();
       // Private();
    }
};

int  main(){
    Simple t;
    Drivce d;
     d.DrivceTest();
    // d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
    // d.Protected();
}

在这里插入图片描述
小结

  • public 无论类内部还是类对象都可以访问。
  • protected 类对象不可访问,类内部与继承类的内部可以访问。
  • private 只有类内部可以访问。

3. 继承关系的构造顺序

  • 派生类的构造函数与析构函数的调用顺序
  • 派生类的构造函数调用顺序
    父对象构造、成员变量(子类依赖的类)析构、子对象构造的顺序
  • 派生类的析构函数调用顺序
    子对象析构、成员变量(子类依赖的类)析构、父对象析构的顺序
    即构造顺序:(1)父类;(2)子类依赖的类;(3)子类;析构顺序刚好相反;
  • 注意:默认构造函数可以不传递参数,父类和子类都可以;但是当父类的构造函数为带参函数时,子类需要在构造函数中初始化列表;
Son():m(1),Father(1){cout << __func__ <<endl;}  //a.默认构造函数可以不传递参数,父类和子类都可以;但是当父类的构造函数为带参函数时,子类需要在构造函数中初始化列表;
  • 完整案例
#include <iostream>
using namespace std;

class Member{
public:
	Member(int i){cout << __func__ <<endl;}
	~Member(){cout << __func__ <<endl;}

};
class Father{
public:
	Father(int i){cout << __func__ <<endl;}
	~Father(){cout << __func__ <<endl;}

};
class Son:public Father{
	Member m;
public:
	Son():m(1),Father(1){cout << __func__ <<endl;}  //a.默认构造函数可以不传递参数,父类和子类都可以;但是当父类的构造函数为带参函数时,子类需要在构造函数中初始化列表;
	~Son(){cout << __func__ <<endl;}

};



int main(){
	
	Son  s;                      //b.构造顺序:(1)父类;(2)子类依赖的类;(3)子类;
                                      //析构顺序刚好相反;
}
没有默认构造函数的基类在派生类的初始化,必须在初始化列表中初始化。

4. 同名隐藏规则

a. 概念:子类的成员函数与基类成员函数同名,子类的函数将会隐藏基类的所有同名函数。

同名隐藏:如果子类与父类同名,子类会隐藏父类的成员函数;一方面,子类会隐藏父类的值,另一方面会调用自己的函数,从而引起类型不匹配;

函数匹配的方法,避免深层查找:(1)先子类后父类,找到,匹配;(2)名和参数进行匹配;(3)参数不匹配,则不再寻找

解决方法1:可以完全解决同名隐藏,实际调用中加入类名(调用父类)

s.Father::Test(2);  // 解决方法一:可以完全解决同名隐藏(调用父类)

解决方法2:子类里面采用命名空间,解决名称相同,但是参数列表不同的情况;main()函数直接用子类对象调用;

(子类)using Father::Test;  //b.2子类里面采用命名空间,解决名称相同,但是参数列表不同的情况;main()函数直接用子类对象调用;

(main函数)s.Test(3);
  • 完整案例
#include <iostream>
using namespace std;

class Father{
public:
void Test(int i){
	cout << "Father::Test(" << i <<")" << endl;
}
void Test(){
	cout << "Father::Test" << endl;
	}
};

class Son:public Father{
public:
	using Father::Test;  //b.2采用命名空间,解决名称相同,但是参数列表不同的情况;
	void Test(){
		cout << "Son::Test" << endl;
	}
};




int main(){
	Father f;
	f.Test(1);
	Son s;
	s.Test();
	s.Father::Test(2);  //b.1 解决之道1;可以完全解决同名隐藏(调用父类)
 	s.Test(3);

}
  • b. 不建议使用同名隐藏规则
    可以在表示普遍方法中的特例中使用,例如直角三角形求面积
  • 完整代码
#include <iostream>
#include <cmath>
using namespace std;

class Triangle{
//private: // 私有成员子类不能访问
protected: // 保护成员变量可以被子类访问
    int a,b,c;
public:
    Triangle(int a,int b,int c):a(a),b(b),c(c){}
    int GetLength()const{
        return a+b+c;
    }
    float GetArea()const{
        float p = (a+b+c)/2.0;
        return sqrt(p*(p-a)*(p-b)*(p-c));
    }
    // int GetA()const{return a;}
};

// EqualTriangle is a Triangle
class EqualTriangle:public Triangle{
public:
      EqualTriangle(int bottom,int side):Triangle(bottom,side,side){}

      int GetBottom()const{
        return a;
      }
      int GetSide()const{
        return b;
      }

};
class RightTriangle:public Triangle{
public:
      RightTriangle(int a,int b):Triangle(a,b,sqrt(a*a+b*b)){}
      float GetArea()const{
        cout << "RightTriangle::GetArea()" << endl;
        return a*b/2.0;
      }
};

int main(){
    cout << sizeof(EqualTriangle) << endl;
    EqualTriangle e(10,15);
    cout << e.GetLength() << endl;
    cout << e.GetArea() << endl;
    cout << e.GetBottom() << endl;
    cout << e.GetSide() << endl;
    Triangle t(3,4,5);
    cout << t.GetArea() << endl;
    RightTriangle r(3,4);
    cout << r.GetArea() << endl;
}

5. 函数同名的情况总结

名称英语
重载overload
重写(覆盖)override
隐藏hide

6. 赋值兼容

  • 赋值兼容规则(和多态相关)
    概念:在任何需要基类对象的地方都可以使用公有的派生类对象来代替。反之,不可。
    father 可以是儿子,但是儿子不可以是father;
    子类就相当于父类的对象;

三种情况

情况1. 派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。

//1.Base类
Base& operator=(const Base&){cout<<__func__<<endl;} //a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。

//(2.main)
    b = d;    
	//d = b;  a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。

对象切割(Object Slicing):在赋值时舍弃派生类自己的成员,只进行基类数据成员的赋值。(子类的成员则不接受,父类)

  • 总结
    对象赋值兼容的时机(调用拷贝构造函数)
    a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
    b. 函数参数对象传值
//(main)
Base b2(d);    //b1.函数参数对象传值

c. 函数返回对象

//类外
void Func(Base b){}
Base Func(){
	Derive d;
	return d;  //b2.函数返回对象
	
}

d. 注意:(1)加入默认构造函数;
(2)拷贝构造函数初始化列表。

//base类
Base():n(0){}
Base(const Base& b):n(b.n){cout << __func__<<endl;}
  • 完整案例
#include <iostream>
using namespace std;


class Base{
public:
	int n;
	Base():n(0){}
	Base(const Base& b):n(b.n){cout << __func__<<endl;}
	//Base& operator=(const Base&){cout<<__func__<<endl;} //a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
};

class Derive:public Base{
public:
	int m; 
	void Test(){
	cout << __func__ <<endl;
	}
};

//b对象赋值兼容的时机(调用拷贝构造函数)
//b1.函数参数对象传值
//b2.函数返回对象
//b3.注意:(1)加入默认构造函数;
	//(2)拷贝构造函数初始化列表。

void Func(Base b){}
Base Func(){
	Derive d;
	return d;  //b2.函数返回对象

}

int main(){
	Base b;
	Derive d;
	d.n=1;
	d.m=10;
	b = d;    
	//d = b;  a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
	cout << b.n << endl;
	//cout << b.m << endl;
	cout << sizeof(b) << endl;
	cout << sizeof(d) << endl;
	Base b2(d);    //b1.函数参数对象传值
	cout << b2.n <<endl; 
	Func(b);
	Func(d);
	Func();

}
问题:
子类特有的成员变量能否被父类访问? 否
此时父类对象(base)的大小与子类对象(derive)大小是否一致? 否
设计一段代码验证(见上)

情况2. 派生类的对象可以初始化基类的引用。
a.派生类的对象可以初始化基类的引用

Derive d;
Base& fb = d;   //a.派生类的对象可以初始化基类的引用

b.二者的地址是相同的;

cout << &d <<endl;  //b.二者的地址是相同的;
cout << &fb <<endl;

c.类型决定大小和能否访问成员
大小:

cout << sizeof(d) <<endl; //c1.类型大小不同,d的大小
cout << sizeof(fb) <<endl; //c2. base的大小

访问:

d.Test(); //c3.
//c4.fb.Test();  //类型决定大小和能否访问成员
#include <iostream>
using namespace std;

class Base{
public:
	int n;
	Base():n(0){}
	Base(const Base& b):n(b.n){cout << __func__<<endl;}
	//Base& operator=(const Base&){cout<<__func__<<endl;}
};

class Derive:public Base{
public:
	int m; 
	void Test(){
	cout << __func__ <<endl;
	}
};

int main(){
	Derive d;
	Base& fb = d;   //a.派生类的对象可以初始化基类的引用
	cout << &d <<endl;  //b.二者的地址是相同的;
	cout << &fb <<endl;
	cout << sizeof(d) <<endl; //c1.类型大小不同,d的大小
	cout << sizeof(fb) <<endl; //c2. base的大小
	d.Test(); //c3.父类虽然指向子类对象,但是不能访问子类成员;
	//c4.fb.Test();  //类型决定大小和能否访问成员
}

情况3. 派生类对象的地址可以赋给指向基类的指针。
指向基类对象的指针变量也可以指向派生类对象。
父类引用子类对象与父类指针指向子类对象,都只能访问子类继承的那部分成员,用法相同,写法不同;

正确写法:

 Base* pb = &d;
 Func(&d);  //Func(pb); 

错误写法:

pb->Test();
pb->m;    
	//父类引用子类对象与父类指针指向子类对象,都只能访问子类继承的那部分成员,用法相同,写法不同;
  • 完整案例
#include <iostream>
using namespace std;


class Base{
public:
	int n;
	Base():n(0){}
	Base(const Base& b):n(b.n){cout << __func__<<endl;}
	//Base& operator=(const Base&){cout<<__func__<<endl;}
};

class Derive:public Base{
public:
	int m; 
	void Test(){
	cout << __func__ <<endl;
	}
};

void Func(Base& b){}
void Func(Based* b){}


int main(){
	Derive d;
	Base& fb = d;   //a.派生类的对象可以初始化基类的引用
	cout << &d <<endl;  //b.二者的地址是相同的;
	cout << &fb <<endl;
	cout << sizeof(d) <<endl; //c1.类型大小不同,d的大小
	cout << sizeof(fb) <<endl; //c2. base的大小
	d.Test(); //c3.父类虽然指向子类对象,但是不能访问子类成员;
	//c4.fb.Test();  //类型决定大小和能否访问成员
	Func(d);
	Base* pb = &d;
	//pb->Test();
	//pb->m;    
	//父类引用子类对象与父类指针指向子类对象,都只能访问子类继承的那部分成员,用法相同,写法不同;
	Func(&d);

}
向上转换:派生类对象赋值给基类
向下转换:基类对象赋值给派生类

(2)其他其情况

  • 派生类的对象可以赋值给基类的对象
 int main() {
          Base base;
          Derive derive;
          base = derive;
          base.show(123);
          //base.show();
          //base.Derive::show();
    }
  • 派生类对象的地址赋给基类的指针变量
  int main() {
          Base* pbase = new Derive;
          pbase->show(123);
          //pbase->show();
          //pbase->Derive::show();
    }
指针访问派生类中由基类继承来的对象,不能访问派生类中的新成员
  • 派生类对象可以初始化基类的引用
  int main() {
          Derive derive;
          Base& base = derive;
          base.show(123);
          //base.show();
          //base.Derive::show();
    }
引用访问派生类中由基类继承来的对象,不能访问派生类中的新成员

(3)多重继承
一个类可以同时继承多个父类的行为和特征功能。
逗号分割的基类列表

class 类名 : public 基类1,public 基类2{
};
  • 案例一:等腰直角三角形继承等腰三角形与直角三角形
  • 注意:多重继承的构造函数的写法(利用两个父类都进行构造函数初始化列表)
EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right){}   //多重继承的构造函数的写法(利用两个父类都进行构造函数初始化列表)
  • 完整案例
#include <iostream>
#include <cmath>
using namespace std;

class Triangle{
//private: // 私有成员子类不能访问
protected: // 保护成员变量可以被子类访问
    float a,b,c;
public:
    Triangle(float a,float b,float c):a(a),b(b),c(c){}
    float GetLength()const{
        return a+b+c;
    }
    float GetArea()const{
        float p = (a+b+c)/2.0;
        return sqrt(p*(p-a)*(p-b)*(p-c));
    }
    // int GetA()const{return a;}
};

// EqualTriangle is a Triangle
class EqualTriangle:public Triangle{
public:
      EqualTriangle(float bottom,float side):Triangle(bottom,side,side){}

      float GetBottom()const{
        return a;
      }
      float GetSide()const{
        return b;
      }

};
class RightTriangle:public Triangle{
public:
      RightTriangle(float a,float b):Triangle(a,b,sqrt(a*a+b*b)){}
      float GetArea()const{
        cout << "RightTriangle::GetArea()" << endl;
        return a*b/2.0;
      }
      float GetSide()const{
      	return c;
      }
};

class EqualRightTriangle:public EqualTriangle,public RightTriangle{
public:
	EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right){}   //多重继承的构造函数的写法(利用两个父类都进行构造函数初始化列表)

};


int main(){
    cout << sizeof(EqualTriangle) << endl;
    EqualTriangle e(10,15);
    cout << e.GetLength() << endl;
    cout << e.GetArea() << endl;
    cout << e.GetBottom() << endl;
    cout << e.GetSide() << endl;
    Triangle t(3,4,5);
    cout << t.GetArea() << endl;
    RightTriangle r(3,4);
    cout << r.GetArea() << endl;
    
    EqualRightTriangle er(1);
    cout << er.GetCside()<<endl;
    cout << er.GetSide()<<endl;
    cout << er.GetBottom()<<endl;

}

案例二:多重继承基类构造顺序?
a. 父类在子类中的继承顺序就是对象中父类的构造顺序;

class C:public A,public B{}  //a.父类在子类中的继承顺序就是对象中父类的构造顺序;

b. 同一个父类的多个子类,构造顺序为初始化顺序;

class A:public Base{}               //b.同一个父类的多个子类,构造顺序为初始化顺序;
class B:public Base{}
  • 完整案例
#include <iostream>
using namespace std;

class Base{
public:
	Base(){cout << __func__ <<endl;}
	~Base(){cout << __func__ <<endl;}
};

class A:public Base{               //b.同一个父类的多个子类,构造顺序为初始化顺序;
public:
	A(){cout << __func__ <<endl;}
	~A(){cout << __func__ <<endl;}
};
class B:public Base{
public:
	B(){cout << __func__ <<endl;}
	~B(){cout << __func__ <<endl;}

};
class C:public A,public B{   //a.父类在子类中的继承顺序就是对象中父类的构造顺序;
public:
	C(){cout << __func__ <<endl;}
	~C(){cout << __func__ <<endl;}
};
int main(){
	C c;

}

(4)钻石继承/菱形继承

  • 概念:两个子类继承同一个父类,而又有子类同时继承这两个子类。
  • 大小:孙类的大小等于父类之和
cout << sizeof(Triangle) << endl; //12
cout << sizeof(EqualTriangle) << endl; //12
cout << sizeof(RightTriangle) << endl; //12
cout << sizeof(EqualRightTriangle) << endl; //24 b.大小:孙类的大小等于父类之和

问题:

  1. 同名变量如何进行访问(权限符);
    注意:变量的顺序为(第二个继承父类的倒序)
cout << "a:" << EqualTriangle::a <<endl;  //1.相同变量如何进行访问(权限符);
		cout << "b:" << EqualTriangle::b <<endl;
		cout << "c:" << EqualTriangle::c <<endl;
		cout << "a:" << RightTriangle::a <<endl;//1.相同变量如何进行访问(权限符);倒序
		cout << "b:" << RightTriangle::a <<endl;
		cout << "c:" << RightTriangle::a <<endl;
  1. 同名的函数如何进行访问;(权限符)
er.Print();
    cout << er.EqualTriangle::GetLength() << endl;  
    cout << er.RightTriangle::GetLength() << endl; // 同名的函数如何进行访问;(权限符)
  • 完整案例
#include <iostream>
#include <cmath>
using namespace std;

class Triangle{
//private: // 私有成员子类不能访问
protected: // 保护成员变量可以被子类访问
    float a,b,c;
public:
    Triangle(float a,float b,float c):a(a),b(b),c(c){}
    float GetLength()const{
        return a+b+c;
    }
    float GetArea()const{
        float p = (a+b+c)/2.0;
        return sqrt(p*(p-a)*(p-b)*(p-c));
    }
    // int GetA()const{return a;}
};

// EqualTriangle is a Triangle
class EqualTriangle:public Triangle{
public:
      EqualTriangle(float bottom,float side):Triangle(bottom,side,side){}

      float GetBottom()const{
        return a;
      }
      float GetSide()const{
        return b;
      }

};
class RightTriangle:public Triangle{
public:
      RightTriangle(float a,float b):Triangle(a,b,sqrt(a*a+b*b)){}
      float GetArea()const{
        cout << "RightTriangle::GetArea()" << endl;
        return a*b/2.0;
      }
      float GetCside()const{
      	return c;
      }
};

class EqualRightTriangle:public EqualTriangle,public RightTriangle{
public:
	EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right){}
	void Print(){
		cout << "a:" << EqualTriangle::a <<endl;  //c.1相同变量如何进行访问(权限符);
		cout << "b:" << EqualTriangle::b <<endl;
		cout << "c:" << EqualTriangle::c <<endl;
		cout << "a:" << RightTriangle::a <<endl;//c.1相同变量如何进行访问(权限符);倒序
		cout << "b:" << RightTriangle::a <<endl;
		cout << "c:" << RightTriangle::a <<endl;
	}
};


int main(){
    cout << sizeof(EqualTriangle) << endl;
    EqualTriangle e(10,15);
    cout << e.GetLength() << endl;
    cout << e.GetArea() << endl;
    cout << e.GetBottom() << endl;
    cout << e.GetSide() << endl;
    Triangle t(3,4,5);
    cout << t.GetArea() << endl;
    RightTriangle r(3,4);
    cout << r.GetArea() << endl;


    EqualRightTriangle er(1);
    cout << er.GetCside()<<endl;
    cout << er.GetSide()<<endl;
    cout << er.GetBottom()<<endl;

    cout << sizeof(Triangle) << endl; //12
    cout << sizeof(EqualTriangle) << endl;  //12
    cout << sizeof(RightTriangle) << endl;  //12
    cout << sizeof(EqualRightTriangle) << endl; //24 b.大小:孙类的大小等于父类之和
	
	er.Print();
    cout << er.EqualTriangle::GetLength() << endl;  
    cout << er.RightTriangle::GetLength() << endl;   //c.2 同名的函数如何进行访问;(权限符)

}

解决变量名冲突虚继承

  • 虚继承&虚基类
    虚继承:在继承定义中包含了 virtual 关键字的继承关系。
    虚基类:在虚继承体系中的通过 virtual 继承而来的基类。
class 类名:public virtual 基类{
}

总结
a.虚继承需要将父类的public改为 public virtual;孙类仍为public;

class EqualTriangle:public virtual Triangle{   //a.虚继承需要将父类的public改为 public virtual;孙类仍为public;

b.孙类的构造函数的初始化列表需要加上爷类的初始化;

EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right),Triangle(right,right,sqrt(right*right*2)){}   //b.孙类的构造函数的初始化列表需要加上爷类的初始化;

c.大小:因为引入了虚继承,每一次继承需要加一个指针的大小,并填充,孙类则加上继承数*指针大小(8个字节),并填充;

cout << sizeof(Triangle) << endl;   //24
    cout << sizeof(EqualTriangle) << endl;//24
    cout << sizeof(RightTriangle) << endl;//24 c.大小:因为引入了虚继承,每一次继承需要加一个指针的大小,并填充,孙类则加上继承数*指针大小(8个字节),并填充;

    cout << sizeof(EqualRightTriangle) << endl;//32
  • 完整案例
#include <iostream>
#include <cmath>
using namespace std;

class Triangle{
//private: // 私有成员子类不能访问
protected: // 保护成员变量可以被子类访问
    float a,b,c;
public:
    Triangle(float a,float b,float c):a(a),b(b),c(c){}
    float GetLength()const{
        return a+b+c;
    }
    float GetArea()const{
        float p = (a+b+c)/2.0;
        return sqrt(p*(p-a)*(p-b)*(p-c));
    }
    // int GetA()const{return a;}
};

// EqualTriangle is a Triangle
class EqualTriangle:public virtual Triangle{   //a.虚继承需要将父类的public改为 public virtual;孙类仍为public;
public:
      EqualTriangle(float bottom,float side):Triangle(bottom,side,side){}

      float GetBottom()const{
        return a;
      }
      float GetSide()const{
        return b;
      }

};
class RightTriangle: public virtual Triangle{
public:
      RightTriangle(float a,float b):Triangle(a,b,sqrt(a*a+b*b)){}
      float GetArea()const{
        cout << "RightTriangle::GetArea()" << endl;
        return a*b/2.0;
      }
      float GetCside()const{
      	return c;
      }
};

class EqualRightTriangle:public EqualTriangle,public RightTriangle{
public:
	EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right),Triangle(right,right,sqrt(right*right*2)){}   //b.孙类的构造函数的初始化列表需要加上爷类的初始化;
	void Print(){
/*		cout << "a:" << EqualTriangle::a <<endl;
		cout << "b:" << EqualTriangle::b <<endl;
		cout << "c:" << EqualTriangle::c <<endl;
		cout << "a:" << RightTriangle::a <<endl;
		cout << "b:" << RightTriangle::a <<endl;
		cout << "c:" << RightTriangle::a <<endl;
*/
		cout << "a:" << a <<endl;
		cout << "b:" << b <<endl;
		cout << "c:" << c <<endl;
	}
};


int main(){
    cout << sizeof(EqualTriangle) << endl;
    EqualTriangle e(10,15);
    cout << e.GetLength() << endl;
    cout << e.GetArea() << endl;
    cout << e.GetBottom() << endl;
    cout << e.GetSide() << endl;
    Triangle t(3,4,5);
    cout << t.GetArea() << endl;
    RightTriangle r(3,4);
    cout << r.GetArea() << endl;


    EqualRightTriangle er(1);
    cout << er.GetCside()<<endl;
    cout << er.GetSide()<<endl;
    cout << er.GetBottom()<<endl;

    cout << sizeof(Triangle) << endl;   //24
    cout << sizeof(EqualTriangle) << endl;//24
    cout << sizeof(RightTriangle) << endl;//24 c.大小:因为引入了虚继承,每一次继承需要加一个指针的大小,并填充,孙类则加上继承数*指针大小(8个字节),并填充;

    cout << sizeof(EqualRightTriangle) << endl;//32
	
	er.Print();
    cout << er.GetLength() << endl;
    cout << er.RightTriangle::GetLength() << endl;

}

d.虚继承构造函数的调用次数:爷类仅仅调用一次构造函数;

class Base{ // d.虚继承构造函数的调用次数:爷类仅仅调用一次;

e.空类的大小为1个字节,原因:申请最小的内存,编辑器需要预先留下最小的空间,相当于一个占位符;

class Empty{};  //e.空类的大小为1个字节,原因:申请最小的内存,编辑器需要预先留下最小的空间,相当于一个占位符;
  • 完整案例
#include <iostream>
using namespace std;

class Base{ // d.虚继承构造函数的调用次数:爷类仅仅调用一次;
public:
	Base(){cout << __func__ <<endl;}
	~Base(){cout << __func__ <<endl;}
};

class A:public virtual Base{
public:
	A(){cout << __func__ <<endl;}
	~A(){cout << __func__ <<endl;}
};
class B:public virtual Base{
public:
	B(){cout << __func__ <<endl;}
	~B(){cout << __func__ <<endl;}

};
class C:public A,public B{
public:
	C(){cout << __func__ <<endl;}
	~C(){cout << __func__ <<endl;}
};

class Empty{};  //e。空类的大小为1个字节,原因:申请最小的内存,编辑器需要预先留下最小的空间,相当于一个占位符;

int main(){
	C c;
	cout << sizeof(Base) <<endl;
	cout << sizeof(A) <<endl;
	cout << sizeof(B) <<endl;
	cout << sizeof(C) <<endl;
	cout << sizeof(Empty) <<endl;  //1


}
虚基类是一个相对概念,在虚继承关系中,父类相对与子类是虚基类。
  • 问题:下面B、C继承与A,D同时继承B、C,那么同时D的实例调用A的成员函数会有什么情况?
#include <iostream>
using std::cout;
using std::endl;
class A{
    public:
          void test();
    private:
          int id;
    };
void A::test(){
        cout << __func__ << endl;
    }
    class B : public A {};
    class C : public A {};
    class D : public B,public C{};
    int main(){
        cout << "A size:" << sizeof(A) << endl;
        cout << "B size:" << sizeof(B) << endl;
        cout << "C size:" << sizeof(C) << endl;
        cout << "D size:" << sizeof(D) << endl;
        D d;
        d.test();
    } 
  • 原因:B与C都继承了A的成员函数test(),D同时继承了B与C,调用test()无法确定是B还是C的。
  • 解决:给调用的成员函数前加上访问限定符,明确指定调用成员函数所属的类d.B::test();或者d.C::test();。

派生类的编码

  1. 构造与析构
  2. 继承基类成员
  3. 重载基类成员函数
  4. 增加新成员

测验

  • 写出下列程序的执行结果,并分析结果。(如果程序编译有错,请分析原因,并写出解决方法)
#include <iostream>
using namespace std;

class Base{
public:
        Base(){
            cout << "Base constuct" << endl;
        }
        ~Base(){
            cout << "Base destuct" << endl;
        }
    };
class Member{
public:
        Member(){
            cout << "Member constuct" << endl;
        }
        ~Member(){
            cout << "Member destuct" << endl;
        }
    };
class Derive:public Base{
public:
        Derive(){
            cout << "Derive constuct" << endl;
        }
        ~Derive(){
            cout << "Derive destuct" << endl;
        }
private:
     	Member m;
    };
    int main(){
        Derive d;
    }

7.(面试)关于多重继承

  1. 什么是多重继承?同时继承多个父类。
  2. 多重继承有什么危害?菱形继承/钻石继承。
  3. 什么是菱形继承/钻石继承?多重继承的两个或多个父类具有相同的祖先类。
  4. 菱形继承/钻石继承有什么危害?因为多重继承的两个或多个父类具有相同的祖先类。所以会有完全相同的属性和方法。因此当前多重继承类有两份相同的属性和方法。使用时会出现冲突。
  5. 如何解决菱形继承/钻石继承导致的冲突?使用虚继承。
  6. 什么是虚继承?父类在继承具有相同的祖先类时,加上virtual.

8. 对象构造顺序总结

对象构造顺序

  • 基本原则
  1. 先父后子
  2. 从左到右
  3. 先虚后实
  4. 从上到下
  5. 由内及外(先构造成员变量,再构造自己)

在这里插入图片描述

  • 案例
#include <iostream>

using namespace std;

class A{
public:
	A(){cout<<__func__<<endl;}
	~A(){cout<<__func__<<endl;}
};
class B{
public:
	B(){cout<<__func__<<endl;}
	~B(){cout<<__func__<<endl;}
};
class I{
public:
	I(){cout<<__func__<<endl;}
	~I(){cout<<__func__<<endl;}
};
class J{
public:
	J(){cout<<__func__<<endl;}
	~J(){cout<<__func__<<endl;}
};
class K{
public:
	K(){cout<<__func__<<endl;}
	~K(){cout<<__func__<<endl;}
};
class C{
public:
	C(){cout<<__func__<<endl;}
	~C(){cout<<__func__<<endl;}
};
class X{
public:
	X(){cout<<__func__<<endl;}
	~X(){cout<<__func__<<endl;}
};
class Y{
public:
	Y(){cout<<__func__<<endl;}
	~Y(){cout<<__func__<<endl;}
};
class Z{
public:
	
	Z(){cout<<__func__<<endl;}
	~Z(){cout<<__func__<<endl;}
};

class Simple:public I,public J, public K, public virtual X,public virtual Y,public virtual Z{
private:
	A a;
	B b;
	C c;
public:
	
	Simple(){cout<<__func__<<endl;}
	~Simple(){cout<<__func__<<endl;}
};

int main(){
	Simple s;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值