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;
	}
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. <span style='color:red;'>C</span>++<span style='color:red;'>继承</span>

    C++继承

    2024-07-14 09:34:02      53 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-14 09:34:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-14 09:34:02       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-14 09:34:02       58 阅读
  4. Python语言-面向对象

    2024-07-14 09:34:02       69 阅读

热门阅读

  1. C++多态

    C++多态

    2024-07-14 09:34:02      23 阅读
  2. B树:深入解析与实战应用

    2024-07-14 09:34:02       24 阅读
  3. C语言调用python

    2024-07-14 09:34:02       25 阅读
  4. pytorch GPU cuda 使用 报错 整理

    2024-07-14 09:34:02       25 阅读
  5. 大语言模型LLM

    2024-07-14 09:34:02       21 阅读
  6. 大模型时代,还需要跨端framework吗?

    2024-07-14 09:34:02       27 阅读