C++基础——继承(下)

一、继承与静态成员

基类定义了static 静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子

类,都只有一个 static 成员实例 。


class person
{
public:
	person(const char* name = "lisi")
	:_name(name)
	{}
public:
	string _name;
    // 静态成员属性
	static int count;
};
class worker : public person
{
public:
 
protected:
	string _job_number = "111";
};
// 静态成员属性需要在类外定义
int person::count = 0;
 
void Test2(void)
{
	person pobj;
	worker wobj;
 
    pobj._name = "wangwu";
	cout << pobj._name << endl;
	cout << wobj._name << endl;
 
	cout << "wobj.count: " << wobj.count << endl;
	// 基类更改这个静态成员
	pobj.count = 5;
	cout << "wobj.count: " << wobj.count << endl;
 
	cout << "&pobj.count = " << &pobj.count << endl;
	cout << "&wobj.count = " << &wobj.count << endl;
}

静态成员属性需要在类外定义;

上面的_name,基类对象和派生类对象各自私有一份,而对于静态成员变量 count,派生类继承的 count 和基类里面的 count 是同一份。

即基类里面的静态成员,无论有多少个派生类,它们都共享同一个静态成员。

二、单继承

单继承:一个派生类只有一个直接基类,我们称这个继承关系为单继承。

class Person{};
class Worker : public Person{};
class Teacher : public Worker{};

三、多继承

多继承:一个派生类有两个或以上直接基类时称这个继承关系为多继承。

四、菱形继承

类似以上继承为菱形继承。

菱形继承会产生数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。

二义性无法明确知道访问的是哪一个,需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决。

那么该怎么解决呢?

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。

五、虚拟继承

为解决菱形继承的二义性和数据冗余,我们采用菱形继承,我们看以下代码:

class A
{
public:
	int _a;
};
// class B : public A
class B : virtual public A
{
public:
	int _b;
};
// class C : public A
class C : virtual public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

六、总结

1.很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。

2.多继承可以认为是C++的缺陷之一,很多后来的语言都没有多继承,如Java

3.继承和组合的区别?什么时候用继承?什么时候用组合?


继承和组合是面向对象编程中两种常见的代码重用机制,它们有不同的使用场景和特点。

继承:

  • 继承是一种“is-a”关系,它描述了两个类之间的一种层次结构,子类继承了父类的属性和方法,并且可以添加自己的新属性和方法。
  • 适合于在现有类的基础上进行扩展,通过重用现有类的代码来实现新类的功能。
  • 通常用于描述一种分类或分类的关系,子类是父类的特例,具有更具体的特征或行为。
  • 适用于需要实现代码的重用和扩展的情况。

组合:

  • 组合是一种“has-a”关系,它描述了两个类之间的一种包含关系,一个类包含另一个类的实例作为其成员变量。
  • 适合于描述一种包含关系,其中一个类包含另一个类的实例,并且通过这种组合来实现更复杂的功能。
  • 通常用于描述一种组合或拥有关系,其中一个类包含了另一个类的实例作为其一部分。
  • 适用于需要将不同的类组合在一起实现某个功能的情况,而不是通过继承来实现。

在未来代码设计中,遵循的设计原则是:低耦合,高内聚。

在选择继承还是组合时,可以考虑以下几点:

  • 代码重用性:如果需要重用现有类的代码并扩展其功能,则可以选择继承。如果只是需要利用现有类的功能而不需要扩展其功能,则可以选择组合。
  • 耦合性:继承会增加类之间的耦合性,子类与父类之间存在较强的依赖关系,而组合可以降低耦合性,类之间的关系更灵活。
  • 设计灵活性:组合比继承更灵活,因为可以随时更改组合关系,而不会影响类的结构。继承则更加静态,子类的结构受限于父类的定义。

4.如何定义一个无法被继承的类?

第一种方式C++98,将基类的构造私有化,派生类继承这个基类,在实例化对象时,需要调用基类的构造,但由于基类的构造已经私有化,故会编译报错。

class A
{
public:
    
//将基类的构造函数私有化
private:   
	A(int a = int())
	:_a(a)
	{
		cout << "A()" << endl;
	};
protected:
	int _a;
};
 
class B : public A
{
protected:
	int _b;
};

对于 C++11 的做法是:通过关键字 final,被final修饰的类无法被继承,编译器会强制检查。

// 用 final 修饰 A类, 此时A类无法被继承
class A final
{
public:
	A(int a = int()) 
	:_a(a)
	{
	    cout << "A()" << endl;
	}
protected:
	int _a;
};
 
class B : public A
{
protected:
	int _b;
};

  • 32
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值