c++ inheritance 继承相关总结&备忘

继承相关知识总结:

private继承对派生类不可见(exist but not accessable),通过using Base::someFunc; 可以 access 基类的成员(前提是someFunc在基类中是public属性或者protected属性——因为private属性是不能被继承的).

private 和 protect 继承不允许使用 基类指针 指向 派生类(编译error: 'BaseClass' is an inaccessible base of 'DerivedClass'

reinterpret_cast可以将private继承的派生类指针强制转化为基类指针(通过pBase = static_cast <BaseClass*>(pDrived);但是不要这么做,因为违反了pravite inheritance的原则!(dynamic_cast 不行,有人说static_cast 也可以,但是我试了不行:gcc4.8.1,c++11)


pravite继承类似于在基类添加了一个private成员变量——聚合(相似,但有区别)尊重原作,原文链接——effective c++ 条款39:

//E.g., the “Car has-a Engine” relationship can be expressed using simple composition
class Engine {
public:
  Engine(int numCylinders);
  void start();                 // Starts this Engine
};
class Car {
public:
  Car() : e_(8) { }             // Initializes this Car with 8 cylinders
  void start() { e_.start(); }  // Start this Car by starting its Engine
private:
  Engine e_;                    // Car has-a Engine
};

//The “Car has-a Engine” relationship can also be expressed using private inheritance:
class Car : private Engine {    // Car has-a Engine
public:
  Car() : Engine(8) { }         // Initializes this Car with 8 cylinders
  using Engine::start;          // Start this Car by starting its Engine
};

//使用 composition 还是 private inheritance?
//Use composition when you can, private inheritance when you have to
//"Isa" relationship should be modelled by inheritance, "has-a" should be modelled by containment:
class B : public A     // B "is-a" A 
{ 
  ... 
} 

class B 
{ 
  ... 
  private: 
    A a_;     // B "has-a" A 
} 
//While public inheritence model an "is-a" relationship,
//private inheritance doesn't model anything at all, 
//and is purely an implementation construct for sharing code with the class being inherited. 
//This is better achieved by containment.

There are several similarities between these two variants:

In both cases there is exactly one Engine member object contained in every Car object
In neither case can users (outsiders) convert a Car* to an Engine*
In both cases the Car class has a start() method that calls the >start() method on the contained Engine object.

There are also several distinctions:

The simple-composition variant is needed if you want to contain several Engines per Car
The private-inheritance variant can introduce unnecessary multiple inheritance
The private-inheritance variant allows members of Car to convert a Car* to an Engine*
The private-inheritance variant allows access to the protected members of the base class
The private-inheritance variant allows Car to override Engine’s virtual functions
The private-inheritance variant makes it slightly simpler (20 characters compared to 28 characters) to give Car a start() method that simply calls through to the Engine’s start() method
Note that private inheritance is usually used to gain access into the protected members of the base class, but this is usually a short-term solution。

  • Or A member (either data member or member function) declared in a protected section of a class can only be accessed by member functions and friends of that class, and by member functions and friends of derived classes,这就是为什么private继承可以访问protected的基类成员).

派生类指针转基类指针example: &ojb = this,一般来说dynamic_cast效率比较低,尽量少用。effective c++ 条款27,以下代码原文链接

class Animal { /* Some virtual members */ }
class Dog: public Animal {};
class Cat: public Animal {};


Dog     dog;
Cat     cat;
Animal& AnimalRef1 = dog;  // Notice no cast required. (Dogs and cats are animals).
Animal& AnimalRef2 = cat;
Animal* AnimalPtr1 = &dog;
Animal* AnimlaPtr2 = &cat;

Cat&    catRef1 = dynamic_cast<Cat&>(AnimalRef1);  // Throws an exception  AnimalRef1 is a dog
Cat*    catPtr1 = dynamic_cast<Cat*>(AnimalPtr1);  // Returns NULL         AnimalPtr1 is a dog
Cat&    catRef2 = dynamic_cast<Cat&>(AnimalRed2);  // Works
Cat*    catPtr2 = dynamic_cast<Cat*>(AnimalPtr2);  // Works

// This on the other hand makes no sense
// An animal object is not a cat. Therefore it can not be treated like a Cat.
Animal  a;
Cat&    catRef1 = dynamic_cast<Cat&>(a);    // Throws an exception  Its not a CAT
Cat*    catPtr1 = dynamic_cast<Cat*>(&a);   // Returns NULL         Its not a CAT.

很多情况下,如果需要使用继承属性,则将数据定义为private,并且提供protected的接口,这样派生类可以定义自己的私有数据并且不会与基类的数据冲突,而且可以访问到基类的数据,更重要的是,在以后你需要对基类数据成员进行修改的时候不会使得派生类出错,这样可以最大化代码复用(只要不改变protected接口)。如果代码比较少,但是需要用到继承属性,可以定义少量protected数据,这样就是省去了定义一堆接口,简化代码。


要点:

在设计函数的时候Pass-by-value should be avoided for objects(传递const引用,而不是传值):

myMethod (const SomeClass &object)  // good
myMethod (SomeClass object)			// bad: don't do this
  • 效率问题
  • 如果参数是基类,则派生类会被截断。因为在传值的时候,基类的复制构造函数被调用(其参数为派生类对象),所以派生类的部分被切割掉了——effective c++ 第20条: 宁以 pass-by-reference-to-const 替换 pass-by-value。 原因: objects passed by value through a base-class variable will in effect behave as a base-class object without the extended information defined by the derived class

虚析构函数: 如果基类存在虚函数,则基类的析构函数必须是函数,防止内存泄漏(部分析构) 。

Interface: A class with no data and where all functions are pure virtual functions(一般为public) is often called an interface.

纯虚函数的实现 派生类必须重新定义纯虚函数,但是有些共性的代码可以在纯虚函数中的实现完成,例如,可以用来执行某些具有共性的缺省设置,在派生类中显式执行BaseClass::pure_virtual_func即可;缺省设置也可以通过在基类中定义protected的成员函数,然后派生来能够在其成员函数中调用基类的protected函数来完成缺省设置。

派生类调用基类函数

void Der::f()
{
  Base::f();  // Or, if you prefer, this->Base::f();
}

Virtual Constructor? 就是纯虚函数的 clone(复制) 和 create(构造),但是在派生来中返回的为派生类的指针,而不是基类指针。That’s how to define a copy constructor or assignment operator for a class that contains a pointer to a (abstract) base class:

class Shape {
public:
  virtual ~Shape() { }                 // A virtual destructor
  virtual void draw() = 0;             // A pure virtual function
  virtual void move() = 0;
  // ...
  virtual Shape* clone()  const = 0;   // Uses the copy constructor
  virtual Shape* create() const = 0;   // Uses the default constructor
};
class Circle : public Shape {
public:
  Circle* clone()  const;   // Covariant Return Types; see below
  Circle* create() const;   // Covariant Return Types; see below
  // ...
};
Circle* Circle::clone()  const { return new Circle(*this); }
Circle* Circle::create() const { return new Circle();      }

绝不重新定义继承而来的non-virtual函数:Never hide member functions that were public in my base class——effective c++条款36

class B{
public:
	void mf();
}
class D: public B{
public:
	void mf();  //遮掩了 B::mf; 
	//不管是 public、private还是protected,不管函数签名怎么样,
	//只要基类mf不是虚函数,而派生类重新定义了就是遮掩。
};
D x;
B* pB = &x;
pB->mf();	// 调用 B::mf

D* pD = &x;
pD->mf();	// 调用 D::mf

如果不是通过指针调用,而是通过对象x调用,则只可能调用D::mf,即使基类的函数签名不同,而调用跟基类函数签名对应的函数版本(继承中的重载?)也会失败。因为这种遮掩只通过函数名来判断,即函数的名字(mf)。
这种名称查找规则:首先派生类作用域->基类作用域->最后在全局作用域;因此,除非派生类中没有mf函数,基类的mf函数才会被调用,基类中也没有则调用全局的mf函数——effective c++ 条款24,若所有参数皆需要类型转化,请为此采用non-member函数(即全局函数,或者在命名空间的函数)。


不要以多态方式处理数组,基类和派生类大小不同,数组中取地址不一。因为C++ can’t distinguish between a pointer-to-a-thing and a pointer-to-an-array-of-things——more effective c++ 条款3

class Base {
public:
  virtual void f(); 
};
class Derived : public Base {
public:
  // ...
private:
  int i_;
};
void userCode(Base* arrayOfBase)
{
  arrayOfBase[1].f(); // 此处如果传进来的是派生类,
					  //则编译器会按照基类的大小去在数组中找索引为1的指针,
					  //出现runtime error!
					  //如果使用std::array<Derived, 10> 能在编译的时候报错,而不是运行时出错。
}
int main()
{
  Derived arrayOfDerived[10];
  userCode(arrayOfDerived);
  // ...
}

在继承中使用重载? 基类需要使用重载函数时,一般是通过在基类中定义public的重载非虚函数调用protected的非重载虚函数,或者通过 using Base::func 来实现——effective c++ 条款33:

class Base {
public:
  void f(int x)    { f_int(x); }  // 重载非虚函数
  void f(double x) { f_dbl(x); }  // 重载非虚函数
protected:
  virtual void f_int(int);        // 非重载虚函数
  virtual void f_dbl(double);     // 非重载虚函数
};
//这么做是因为如果直接将重载函数定义为虚函数,
//则如果在派生类中重新定义重载函数中的某一个函数(例如在派生来中定义f(char x),
//则基类重载函数会被隐藏,and you are going to die!!!
//通过上面这样的修改,派生类直接重新定义f_int或者f_dbl就可以了。
//可以这么做,但是不推荐:
class Base {
public:
  void f(double x);
};
class Derived : public Base {
public:
  using Base::f;  // 通过这一句可以使用基类被隐藏的函数: Base::f(double x)
  void f(char c);
};
//通过在基类和派生来中定义名字相同,但是参数不同的函数是不能发生重载的,
//只会其中一个隐藏另一个(取决于调用的指针是基类还是派生类)。
//重载本应该定义在同一个类中。但是可以使用using Base::来将基类中的函数引入到派生类中,但是不推荐这么做。

在派生类中 重写 基类虚函数的 public,protected,private 属性 ? No Problem, 直接见例子:

why 虚函数?因为绝不要在派生类中 覆盖 基类的非虚函数, see above.

修改 access 属性正常吗? Totally legal,如果其行为是你所期望的(破坏了封装)。因为这意味着本来你不期望被直接访问的Foo函数,能够通过基类指针直接访问到。

这个跟非虚函数的覆盖不同,不会出现函数遮掩。

struct Base
{
  virtual ~Base() {}

  virtual void Foo() const = 0; // Public
};

class Child : public Base
{
  virtual void Foo() const {} // Private
};

int main()
{
  Child child;

  child.Foo(); // Won't work. Foo is private in this context.

  static_cast<Base&> (child).Foo(); // Okay. Foo is public in this context.
}

Access is checked at the call point using the type of the expression used to denote the object for which the member function is called (B* in the example above). The access of the member function in the class in which it was defined (D in the example above) is in general not known.

see link: http://stackoverflow.com/questions/2141188/changing-function-access-mode-in-derived-class

需要定义 operator = ?
可以考虑一下 copy-and-swap idiom (就是通过复制构造和swap来实现):https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom

to be continued…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值