c++ - 继承


一、使用继承

//父类
class Parent
{
public:
protected:
	int _a;
};

//子类
class Subclasses: public Parent
{
public:
protected:
	int _b;
};

在这里插入图片描述

二、继承方式与限定访问

继承方式:

继承方式
公有public
保护protected
私有private

限定访问:

限定访问
公有public
保护protected
私有private

父类

class Parent
{
public:
	int _a = 1;
protected:
	int _b = 2;
private:
	int _c = 3;
};

公有继承与限定访问

//公有继承
class Subclasses: public Parent
{
public:
	void test01()
	{
		cout << _a << endl;	//公有+公有 = 公有
		cout << _b << endl;	//公有+保护 = 保护
		cout << _c << endl;	//公有+私有 = 不可见(在子类中不可访问)
	}
protected:
	int _n;
}

在这里插入图片描述

保护继承与访问限定

//保护继承
class Subclasses : protected Parent
{
public:
	void test01()
	{
		cout << _a << endl;	//保护+公有 = 保护
		cout << _b << endl;	//保护+保护 = 保护
		cout << _c << endl;	//保护+私有 = 不可见(在子类中不可访问)
	}
protected:
	int _n;
};

在这里插入图片描述

私有继承与访问限定

//私有继承
class Subclasses : private Parent
{
public:
	void test01()
	{
		cout << _a << endl;	//私有+公有 = 私有
		cout << _b << endl;	//私有+保护 = 私有
		cout << _c << endl;	//保护+私有 = 不可见(在子类中不可访问)
	}
protected:
	int _n;
};

在这里插入图片描述

总结:
1、权限小的碰到权限大的---->权限变小。
2、私有成员被继承时在派生类中也不可见(指的是不可访问)。
3、保护限定在继承中起了关键作用,当父类被实例化时,保护成员在类外不可访问,被子类继承时子类也能够访问。

三、隐藏(重定义)

父类和子类的作用域不同,当子类和父类出现相同变量命名或者成员函数命名(函数名相同即可)相同时子类就会隐藏父类,被子类覆盖的父类成员可以通过作用域限定符来使用。

//父类
class Parent
{
public:
	void test01()
	{
		cout << "class Parent" << endl;
	}
protected:
	int _a = 1;
	int _b = 2;
};

//公有继承
class Subclasses: public Parent
{
public:
	//隐藏了父类中的test01()
	void test01()
	{
		cout << "class Subclasses: public Parent" << endl;
	}
	void test02()
	{
		//隐藏了父类的_a,使用子类_a = 10;
		cout << _a << endl;
		//通过作用域限定符来指定
		cout<<Parent::_a<<endl;
		Parent::test01();
	}
protected:
	int _a = 10;
};

void test()
{
	Subclasses s;
	s.test02();
	s.test01();
}

在这里插入图片描述

四、子类与父类之间的赋值转换

1、子类赋值给父类对象、指针、引用叫做切片或者叫切割。
2、父类赋给子类是不被允许的,但是可以强制类型转换,此时就有可能出现越界访问。
3、特殊语法规则:该类转换与普通变量不一样不会产生临时变量。
在这里插入图片描述
在这里插入图片描述

//父类
class Parent
{
public:
protected:
	int _a = 1;
	int _b = 2;
};

//公有继承
class Subclasses : public Parent
{
public:
	
protected:
	int _c = 10;
};

void test()
{
	//子类
	Subclasses s;

	//赋值给父类对象
	Parent p1 = s;

	//赋值给父类指针
	Parent *p2 = &s;

	//赋值给父类引用
	Parent& p3 = s;


	//强制类型转换
	Parent *p4;
	Subclasses* s1 = (Subclasses*)p4;
}

五、父类默认成员函数在子类中的初始化

1、构造函数
对父类:调用其构造函数。
当父类构造函数是默认构造时可以不用显示,如:

//父类
class Parent
{
public:
	Parent()
	{
		cout << "class Parent" << endl;
	}
protected:
	int _a = 1;
	int _b = 2;
};

//公有继承
class Subclasses : public Parent
{
public:
	Subclasses()
	{
		cout << "class Subclasses : public Parent" << endl;
	}
protected:
	int _c = 10;
};

void test()
{
	Subclasses s;
}

在这里插入图片描述
当父类构造函数不是默认无参构造时,就需要要初始化列表显示写出了,如:

//父类
class Parent
{
public:
	Parent(int a,int b):_a(a),_b(b)
	{
		cout << "class Parent" << endl;
	}
protected:
	int _a = 1;
	int _b = 2;
};

//公有继承
class Subclasses : public Parent
{
public:
	//显示写出
	Subclasses() :Parent(10, 10)
	{
		cout << "class Subclasses : public Parent" << endl;
	}
protected:
	int _c = 10;
};

void test()
{
	Subclasses s;
}

在这里插入图片描述

2、拷贝构造
对父类:调用其拷贝构造
子类需要进行浅拷贝时不需要显示写出,编译器会自动调用父类拷贝构造。
子类需要进行深拷贝时需要显示写出,如:

//父类
class Parent
{
public:
	Parent(){}
	Parent(Parent&p)
	{
		cout << "Parent(Parent&p)" << endl;
	}
protected:
	int _a = 1;
	int _b = 2;
};

//公有继承
class Subclasses : public Parent
{
public:
	Subclasses() :Parent(){}
	Subclasses(Subclasses& s):Parent(s)//用s赋值,传给父类时会切片处理
	{
		cout << "Subclasses(Subclasses& s)" << endl;
		//深拷贝......
	}
protected:
	int *_c = new int[10];
};

void test()
{
	Subclasses s1;
	Subclasses s2(s1);
}

3、赋值运算符重载
对父类:调用其赋值运算符重载函数
子类需要进行浅拷贝时不需要显示写出,编译器会自动调用父类赋值运算符重载函数。
子类需要进行深拷贝时需要显示写出,如:

//公有继承
class Subclasses : public Parent
{
public:
	Subclasses() :Parent() {}
	Subclasses& operator=(Subclasses& s)
	{
		//因为子类的operator=和父类的operator=构成了隐藏关系,所以要指定类域
		Parent::operator=(s);
		cout << "Subclasses& operator=(Subclasses& s)" << endl;
		//深拷贝.....
		return s;
	}
protected:
	int* _c = new int[10];
};

void test()
{
	Subclasses s1;
	Subclasses s2;
	s2 = s1;
}

在这里插入图片描述
4、析构函数
父类析构函数不像其他默认成员函数那样有时候需要显示调用,都是编译器进行隐式调用的,无需我们显示写出。
当子类没有资源清理时,无需写析构函数。
当子类有资源清理时,就跟平常一样写析构函数即可,无需显示调用父类析构函数,如:

class Parent
{
public:
	Parent() {}
	~Parent() 
	{
		cout << "~Parent()" << endl;
	}
protected:
	int _a = 1;
	int _b = 2;
};
//公有继承
class Subclasses : public Parent
{
public:
	Subclasses() :Parent() {}
	~Subclasses()
	{
		cout << "~Subclasses()" << endl;
		//释放内存
	}
protected:
	int* _c = new int[10];
};

void test()
{
	Subclasses s1;
}

在这里插入图片描述
5、子类、父类的构造和析构的顺序
在这里插入图片描述
总结:构造:先父后子,析构:先子后父。

六、继承与友元

友元是不能被继承,如:你父亲的朋友不是你的朋友。

七、父类的静态成员变量

子类继承的静态成员变量和其他子类以及父类都是共用一个。
如:
进行计数:

class Parent
{
public:
	Parent() { _d++; }
	void test01() { cout << _d << endl; }
protected:
	int _a = 1;
	int _b = 2;
	static int _d;
};
int Parent::_d = 0;
//公有继承
class Subclasses : public Parent
{
public:
	Subclasses() :Parent() {}
	void test01() { cout << _d << endl; }
protected:
	int _c ;
};

void test()
{
	//构建两个变量
	Subclasses s1;	//调用一个父类 _d++
	Parent p;	//调用一个父类 _d++
	p.test01();	//_d = 2
	s1.test01();//_d = 2
}

在这里插入图片描述

八、多继承

当一个子类继承了多个父类时,叫多继承
如:

class Parent1
{
public:
	Parent1() { cout << "Parent1()" << endl; }
protected:
	int _a = 1;
	int _b = 2;
};

class Parent2
{
public:
	Parent2() { cout << "Parent2()" << endl; }
protected:
	int _a = 1;
	int _b = 2;
};

//公有继承
class Subclasses : public Parent1, public Parent2
{
public:
	Subclasses() :Parent1(), Parent2() {}
protected:
	int _c;
};

void test()
{
	Subclasses s1;	//调用一个父类 _d++
}

在这里插入图片描述
父类构造的顺序:按照定义时的顺序。
父类析构的顺序:与定义时的顺序相反。

菱形继承:
在这里插入图片描述
菱形继承造成的问题:
冗余和二义性。
如:

class A
{
public:
protected:
	int _a = 0;
	int _b = 0;
};

class B:public A
{
public:
protected:
	int _c = 0;
};

class C :public A
{
public:
protected:
	int _d = 0;
};

class D :public B,  public C
{
public:
void test() { cout << _a << endl }
protected:
	int _e = 0;
};

在这里插入图片描述
在这里插入图片描述

_a,_b命名相同(二义性,在使用时会报变量不明确的错误),在不同作用域下虽然指定作用域可以共存,但是它们的含义是一样的(冗余)。

解决菱形继承问题:
使用虚拟继承
关键字:virtual
在会出现菱形继承的类 :后加入关键字。
如:

class A
{
public:
protected:
	int _a = 0;
	int _b = 0;
};
class B: virtual public A
{
public:
protected:
	int _c = 0;
};
class C : virtual public A
{
public:
protected:
	int _d = 0;
};

class D :public B,  public C
{
public:
	void test()
	{
		cout << _a << endl;
		cout <<_b<<endl;
	}
protected:
	int _e = 0;
};

void test()
{
	D d;
	d.test();
}

在这里插入图片描述
可以正常使用_a,_b,并且只有一份。
虚拟继承的原理:
通过虚基表去实现:
这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
在这里插入图片描述

九、计算子类大小

1、普通继承

子类的大小是父类大小加上子类自身新增成员大小的总和(可能还会受到内存对齐等因素的影响)。

#include <iostream>  
  
class Base {  
public:  
    int a;  
    double b;  
};  
  
class Derived : public Base {  
public:  
    char c;  
};  
  
int main() {  
    std::cout << "Size of Base: " << sizeof(Base) << " bytes\n";  
    std::cout << "Size of Derived: " << sizeof(Derived) << " bytes\n";  
  
    return 0;  
}

对于父类:
在这里插入图片描述

对于子类:
在这里插入图片描述

2、含有虚继承

#include <iostream>  
  
class Base {  
public:  
    int a;  
    double b;  
};  
  
class Derived : virtual public Base {  
public:  
    char c;  
};  
  
int main() {  
    std::cout << "Size of Base: " << sizeof(Base) << " bytes\n";  
    std::cout << "Size of Derived: " << sizeof(Derived) << " bytes\n";  
  
    return 0;  
}

对父类:与上述一样
对子类:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值