【从 C 向 C++ 进阶】- 类 - 22. 多继承

从 C 向 C++ 进阶系列导航


1. 多重继承

C++ 支持多重继承。所谓的多重继承指的是子类同时继承多个父类,此时子类拥有所有父类的所有成员。

继承方式如下:

class Derived : public Baes_A, public Baes_B
{
    ...
}

多重继承会造成不同的父类指针(或引用)指向(或引用)子类对象时,实际所指向(或引用)的子类地址不一致。如此一来,假设程序中想通过判断两个指针是否指向相同的对象时,会发生错误的判定导致程序逻辑错误。而这个问题是无解的。因此, C++ 不提倡使用多重继承,C++ 派送出来的如 JAVA、C# 等都不支持多重继承。

  • 实验:
class Base_A 
{
private:
	int mVar;
};

class Base_B
{
private:
	int mVar;
};

class Derived : public Base_A, public Base_B
{
private:
	int mVar;
};

int main(int argc, char *argv[])
{	
	Derived obj;
	Base_A* p_A = &obj;
	Base_B* p_B = &obj;
	if(reinterpret_cast<void*>(p_A) == reinterpret_cast<void*>(p_B))
	{
		cout << "*p_A equal to *p_B" << endl;
	}
	else
	{
		cout << "p_A = " << p_A << endl;	// p_A = 0x7ffe1e60
		cout << "p_B = " << p_B << endl;	// p_B = 0x7ffe1e64
	}
}

2. 虚继承

在多重继承中,还有一个陷阱,称为菱形继承。所谓的菱形继承通俗描述为,由一个基类派生出派生类 A 与派生类 B,然后派生类 C 再继承于派生类 A 与 B,这样一来便会产生冗余的成员。

  • 实验:
class Base
{
public:
	void Print()
	{
		cout << "in Base" << endl;
	}
};

class Derived_A : public Base
{
};

class Derived_B : public Base
{
};

class Derived_C : public Derived_A, public Derived_B
{
};

int main(int argc, char *argv[])
{	
	Derived_C obj;
	// obj.Print();		// error: request for member ‘Print’ is ambiguous
}

从以上可知,Derived_C 继承了 Derived_A 与 Derived_B,自然而然继承了他们的 Print() 成员函数,此时 Derived_C 中存在两个 Print() 成员函数,函数调用时编译器并不知道应该调用哪一个 Print()。

解决冗余的方法是使用虚继承。虚继承的原理可参考:从内存布局看C++虚继承的实现原理

  • 实验:
class Base
{
public:
	void Print()
	{
		cout << "in Base" << endl;
	}
};

class Derived_A : virtual public Base
{
};

class Derived_B : virtual public Base
{
};

class Derived_C : public Derived_A, public Derived_B
{
};

int main(int argc, char *argv[])
{	
	Derived_C obj;
	obj.Print();		// in Base
}

3. 多继承中的虚函数

当子类继承了多个含虚函数的父类时,子类会产生多个对应的虚函数表,每个虚函数表中分别记录与父类对应的虚函数地址。当通过子类访问父类的虚函数时,如果对父类指针使用强制类型转换为另一个父类指针,此时只会进行单纯的地址传递,不会进行类型检查,进而造成错误的指针指向。因此,应使用 C++ 提供的关键字 dynamic_cast 进行指针类型转换,此时转换会多进行一次类型检查,使得指针指向正确的地址。

  • 实验:
class Base_A
{
public:
	virtual void Print_A()
	{
		cout << "in Base_A" << endl;
	}
};

class Base_B
{
public:
	virtual void Print_B()
	{
		cout << "in Base_B" << endl;
	}
};

class Derived : public Base_A, public Base_B
{
};

int main(int argc, char *argv[])
{	
	cout << "sizeof(Derived) = " 
		<< sizeof(Derived) << endl;	// sizeof(Derived) = 8 ,两个虚函数表指针
		
	Derived obj;
	
	Base_A* p_A = &obj;
	p_A->Print_A();			    	// in Base_A
	// p_A->Print_B();			    // error: ‘class Base_A’ has no member named ‘Print_B’
	
	Base_B* p_B = &obj;
	p_B->Print_B();				    // in Base_B
	// p_B->Print_A();		    	// error: ‘class Base_B’ has no member named ‘Print_A’
	
	p_B = (Base_B*)p_A;
	p_B->Print_B();				    // in Base_A
	
	p_B = dynamic_cast<Base_B*>(p_A);
	p_B->Print_B();				    // in Base_B
}

4. 多继承的正确使用

在工程中,一般使用“单继承 + 多接口”来代替多继承。由于接口是无实例的,因此形式上还是单继承。一般地,会在父类中定义一个成员函数 equal() 来判断指针是否指向当前对象,且关于父类指针类型之间的转换需要使用关键字 dynamic_cast 完成。

  • 示例:
class Base
{
protected:
    int mVar;
	
public:
    Base(int num = 0)
    {
        mVar = num;
    }  
    int GetmVar()
    {
        return mVar;
    }  
    bool equal(Base* obj)
    {
        return (this == obj);
    }
};

class Interface_A
{
public:
    virtual void add(int num) = 0;
    virtual void minus(int num) = 0;
};

class Interface_B
{
public:
    virtual void multiply(int num) = 0;
    virtual void divide(int num) = 0;
};

class Derived : public Base, public Interface_A, public Interface_B
{
public:
    Derived(int num = 0) : Base(num)
    {
    }   
    virtual void add(int num)
    {
        mVar += num;
    }    
    virtual void minus(int num)
    {
        mVar -= num;
    }    
    virtual void multiply(int num)
    {
        mVar *= num;
    }   
    virtual void divide(int num)
    {
        if( num != 0 )
        {
            mVar /= num;
        }
    }
};

int main()
{
    Derived obj(100);
    Derived* p = &obj;
    Interface_A* pInt_A = &obj;
    Interface_B* pInt_B = &obj;
    
    cout << "p->GetmVar() = " << p->GetmVar() << endl;    // p->GetmVar() = 100
    
    pInt_A->add(10);
	cout << "p->GetmVar() = " << p->GetmVar() << endl;    // p->GetmVar() = 110
	 
    pInt_B->divide(11);
	cout << "p->GetmVar() = " << p->GetmVar() << endl;    // p->GetmVar() = 10
	 
    pInt_A->minus(5);
	cout << "p->GetmVar() = " << p->GetmVar() << endl;    // p->GetmVar() = 5
	
    pInt_B->multiply(8);
	cout << "p->GetmVar() = " << p->GetmVar() << endl;    // p->GetmVar() = 40
    
    cout << "(pInt_A == p) is " << p->equal(dynamic_cast<Base*>(pInt_A)) << endl;	// (pInt_A == p) is 1 
    cout << "(pInt_B == p) is " << p->equal(dynamic_cast<Base*>(pInt_B)) << endl;	// (pInt_B == p) is 1 
    
    return 0;
}
发布了60 篇原创文章 · 获赞 36 · 访问量 5936

猜你喜欢

转载自blog.csdn.net/qq_35692077/article/details/99656122