C++中继承的几大关键点

1、概念:

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。

一个新类从已有的类中获得其已有的特性称为继承,被继承的称为父类(Base class)或基类,新产生的类称为派生类或子类。


2、继承的方式:

继承权限规则表

继承方式

基类的public成员

基类的protected成员

基类的private成员

继承引起的访问控制变化

Public

Public

Protected

不可见

基类成员在派生类中访问权限不变

Protected

Protected

Protected

不可见

基类的非私有成员成为子类的保护成员

Private

Private

Private

不可见

基类的所有成员成为子类的私有成员



class Base  
{  
public:  
    Base()  
    {  
        _pri = 1;  
        _pro = 2;  
        _pub = 3;  
    }  
private:  
    int _pri;  
protected:  
    int _pro;  
public:  
    int _pub;  
};  

class Derived1 :public Base  
{  
public:  
    Derived1()  
    {  
        _d1_pri = 4;  
        _d1_pro = 5;  
        _d1_pub = 6;  
    }  
    void showd()  
    {  
        _pub = 0;   // 仍为public成员  
        _pro = 2;   //  仍为protected成员  
        //_pri = 3; // 父类私有成员对子类 不可见  
          
    }  
private:  
    int _d1_pri;  
protected:  
    int _d1_pro;  
public:  
    int _d1_pub;  
};  
void TestPublic()  
{  
    Base b;  
    b._pub = 1;  
    //b._pro = 2;  // 父类对象不可访问父类保护成员  
    //b._pri = 3;  // 父类对象不可访问父类私有成员  
    Derived1 d1;  
    d1._pub = 1; // 子类对象可以访问父类公有成员  
    //d1._pro = 2; //子类对象不可访问父类保护成员  
    //d1._pri = 3; // 子类对象不可访问父类私有成员  
    d1._d1_pub = 4;  
} 

// protected 保护继承  
class Derived2 :protected Base  
{  
public:  
    Derived2()  
    {  
        _d2_pri = 4;  
        _d2_pro = 5;  
        _d2_pub = 6;  
    }  
    void showd()  
    {  
        _pub = 1;   //  变为子类的protected成员  
        _pro = 2;   //  仍为protected成员  
        //_pri = 3; // 父类私有成员对子类不可见  
  
    }  
private:  
    int _d2_pri;  
protected:  
    int _d2_pro;  
public:  
    int _d2_pub;  
};  
void TestProtected()  
{  
    Base b;  
    b._pub = 1;  
    //b._pro = 2;  // 父类对象不可访问父类保护成员  
    //b._pri = 3;  // 父类对象不可访问父类私有成员  
    Derived2 d2;  
    //d2._pub = 1; // 父类的public成员权限被修改为protected,子类对象不可访问  
    //d2._pro = 2; //子类对象不可访问父类保护成员  
    //d2._pri = 3; // 子类对象不可访问父类私有成员  
    d2._d2_pub = 4;  
}  

// private私有继承  
class Derived3 :private Base  
{  
public:  
    Derived3()  
    {  
        _d3_pri = 4;  
        _d3_pro = 5;  
        _d3_pub = 6;  
    }  
    void showd()  
    {  
        _pub = 1;   //  变为子类的privite成员  
        _pro = 2;   //  变为子类的privite成员  
        //_pri = 3; // 父类私有成员对子类不可见  
  
    }  
private:  
    int _d3_pri;  
protected:  
    int _d3_pro;  
public:  
    int _d3_pub;  
};  
void TestPrivate()  
{  
    Base b;  
    b._pub = 1;  
    //b._pro = 2;  // 父类对象不可访问父类保护成员  
    //b._pri = 3;  // 父类对象不可访问父类私有成员  
    Derived3 d3;  
    //d3._pub = 1; // 父类的public成员权限被修改为private,子类对象不可访问  
    //d3._pro = 2; // 父类的protected成员权限被修改为private,子类对象不可访问  
    //d3._pri = 3; // 子类对象不可访问父类私有成员  
    d3._d3_pub = 4;  
} 

3、继承又可以分为单继承,多继承,钻石继承(又叫菱形继承),现在来谈论下三种情况下派生类的布局:

(1)单继承:

#include<iostream>
using  namespace std ;

class  Base
{
public:
	Base(int a= 0, int b= 1):_a(a),_b(b)
	{
		cout << "我是基类的构造函数" <<endl;
	}
	~Base()
	{
		cout << "我是基类的析构函数" << endl;
	}
private:
	int _a;
	int _b;
};

class Derived:public Base
{
public:
	Derived(int c= 2):_c(c)
	{
		cout << "我是派生类的构造函数" << endl;
	}
	~Derived()
	{
		cout << "我是派生类的析构函数" << endl;
	}
private:
	int _c;
};




int main()
{
	Base b;
	Derived d;

	b = d;  //可以
	//d = b;报错

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}




为啥子类对象赋值给父类对象可以,父类对象赋值给子类对象不可以?

原因就是子类对象赋值给父类对象时,子类对象发生对象切片,将子类对象的父类部分赋值给父类对象,但是父类对象赋值给子类对象时,没有办法对子类自己的成员赋值,所以父类对象赋值给子类对象不可以。

(2)多继承

#include<iostream>
using  namespace std ;

class  Base1
{
public:
	Base1(int a= 0, int b= 1):_a(a),_b(b)
	{
		cout << "我是基类的构造函数" <<endl;
	}
	~Base1()
	{
		cout << "我是基类的析构函数" << endl;
	}
public:   //将成员设置为public,好分析结果。
	int _a;
	int _b;
};

class Base2
{
public:
	Base2(int c = 2, int d= 3):_c(c),_d(d)
	{
		cout << "我是第二个基类的构造函数"<< endl;
	}
	~Base2()
	{
		cout << "我是第二个基类的析构函数" << endl;
	}
public:     //将成员设置为public,好分析结果。
	int _c;
	int _d;
};


class Derived:public Base2,public Base1
{
public:
	Derived(int e= 4):_e(e)
	{
		cout << "我是派生类的构造函数" << endl;
	}
	~Derived()
	{
		cout << "我是派生类的析构函数" << endl;
	}
	void Print()
	{
		cout << "a = "<< _a  << ","<< "b = " << _b<< "," 
			<< "c = " << _c << ","<< "d = " << _d << ","<< "e = " << _e<< "," <<endl;
	}
public:  //将成员设置为public,好分析结果。
	int _e;
};


void test()
{
	Derived d;
	d.Print();
}


int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}


(3)  菱形继承:

#include<iostream>
using  namespace std ;

class  Base1
{
public:
	Base1(int a= 0, int b= 1):_a(a),_b(b)
	{
		cout << "我是基类的构造函数" <<endl;
	}
	~Base1()
	{
		cout << "我是基类的析构函数" << endl;
	}
public:   //将成员设置为public,好分析结果。
	int _a;
	int _b;
};

class Derived1:public Base1
{
public:
	Derived1(int c=2):_c(c)
	{
		cout << "我是Derived1的构造函数" << endl;
	}
	~Derived1()
	{
		cout << "我是Derived1的析构函数" << endl;
	}
public:
	int _c;

};

class Derived2:public Base1
{
public:
	Derived2(int d=3):_d(d)
	{
		cout << "我是Derived2的构造函数" << endl;
	}
	~Derived2()
	{
		cout << "我是Derived2的析构函数" << endl;
	}
public:
	int _d;
};

class Derived :public Derived1, public Derived2
{
public:
	Derived(int e=4):_e(e)
	{
		cout << "我是Derived的构造函数" << endl;
	}
	~Derived()
	{
		cout << "我是Derived的析构函数" << endl;
	}
public:
	int _e;
};

void test()
{
	Derived d;
	//d._a = 10; 访问不明确 ,造成这种原因是从base类继承两份base成员,导致访问不明确。
	d.Derived1::_a = 12;  //访问Derived1中的_a.
	d.Derived2::_b = 2;  //访问Derived2中的_a.
}

int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}

派生类Derived对象d成员布局如下:


从派生类对象成员布局可以看出,派生类对象继承了两份Base类成员,所以d._a访问不明确,因此虚继承的语法出现了。

#include<iostream>
using  namespace std ;

class  Base1
{
public:
	Base1(int a= 0, int b= 1):_a(a),_b(b)
	{
		cout << "我是基类的构造函数" <<endl;
	}
	~Base1()
	{
		cout << "我是基类的析构函数" << endl;
	}
public:   //将成员设置为public,好分析结果。
	int _a;
	int _b;
};

class Derived1: virtual public Base1
{
public:
	Derived1(int c=2):_c(c)
	{
		cout << "我是Derived1的构造函数" << endl;
	}
	~Derived1()
	{
		cout << "我是Derived1的析构函数" << endl;
	}
public:
	int _c;

};

class Derived2: virtual public Base1
{
public:
	Derived2(int d=3):_d(d)
	{
		cout << "我是Derived2的构造函数" << endl;
	}
	~Derived2()
	{
		cout << "我是Derived2的析构函数" << endl;
	}
public:
	int _d;
};

class Derived :public Derived1, public Derived2
{
public:
	Derived(int e=4):_e(e)
	{
		cout << "我是Derived的构造函数" << endl;
	}
	~Derived()
	{
		cout << "我是Derived的析构函数" << endl;
	}
public:
	int _e;
};

void test()
{
	Derived d;
	d._a = 10; //可以访问。

	cout << "sizeof(d) = " << sizeof(d) << endl;
	
}

int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}


从图中我们可以看出确实只是继承了一份继承,但是基类的布局和原来的布局不一样,现在基类在最低下,Derived1中插入一个指针,指向一个表,表中存放了Deriverd1相对于基类的偏移量,Derived2也存放一个指针,同样指向一个表,这个表中同样存放了Derived2相对于基类的偏移量。从图中我们看出虚继承内存不一样的机制解决了变量的二义性问题。


4.继承综合案例:

#include<iostream>
using  namespace std ;

namespace
{


class Base
{
public:
	Base(int a):_a(a)
	{
		cout << "我是基类的构造函数" <<endl;
	}
	~Base()
	{
		cout << "我是基类的析构函数" << endl;
	}
	Base(const Base &B)
	{
		cout << "我是基类的拷贝构造函数" << endl;
		_a = B._a;
	}
	Base& operator= (const Base & B)
	{
		_a = B._a;
		return *this;
	}

	friend ostream& operator<< (ostream & out, const Base &B)
	{
		out << B._a ;
		return out;
	}

private:
	int _a;
};

class Derived:public Base
{
public:
	Derived(int a,int b):Base(a),_b(b)  //当基类给出显示的构造函数时,子类必须要初始化基类对象。
	{
		cout << "我是派生类的构造函数"<< endl;
	}
	
	~Derived()
	{
		cout << "我是派生类的析构函数" << endl;
	}

	Derived(const Derived & d):Base(d)  //先调用了基类的拷贝构造函数。
	{
		cout << "我是派生类的拷贝构造函数" << endl;
		_b = d._b;
	}
	Derived& operator= (const Derived & d)
	{
		Base::operator=(d);   //先调用基类的赋值运算符重载。
		_b = d._b;
		return *this;
	}
	friend ostream& operator<< (ostream & out ,const Derived &d)
	{
		
		out << "-------"<< (Base&)d; //先调用基类的重载函数。
		out << "," << d._b ;
		return out;
	}

private:
	int _b;
};

void test()
{
	Derived d(1,2);
	cout << d << endl;
	Derived d2(d);
	cout << d2 <<endl;
	Derived d3(3,4);
	d3 = d;
	cout << d3 <<endl;

}

} //namespace结束。


int main()
{
	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}

5.继承注意点:

(1)父类的构造、析构、拷贝构造、重载赋值运算符,友元函数不能继承。

 (2) 基类没有缺省构造函数,派生类必须要在初始化列表中显式给出基类名和参数列表。
 (3)基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省构造函数。
  (4)、基类定 义了带有形参表构造函数,派生类就一定 定义构造函数 (无参或者带参)
(5)继承时,先调用父类的构造函数,再调用组合对象的构造函数,最后调用自己的构造函数,析构时相反。
(6) public继承是is-a关系,private继承和组合对象是has-a关系。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值