C++ Primer 学习笔记_66_面向对象编程 --定义基类和派生类[续](二)

2014-11-24 12:43:44 · 作者: · 浏览: 1
e_derived { int use_base() { return i; //Error } }; class Derived_from_Public : public Public_derived { int use_base() { return i; //OK } };

其实这也可以理解:因为在类Private_derived中它所继承来的所有东西都变成private的了,这就相当于派生类不能访问基类private成员一样了!而从Public_derived派生的类可以访问来自Base类的i,是因为该成员在Public_derived中仍为protected成员。



1、接口继承与实现继承

public派生类继承基类的接口:它具有与基类相同的接口。设计良好的类层次中,public派生类的对象可以用在任何需要基类对象的地方。【接口继承】

private和protected派生类继承基类的实现:它们“不继承”基类的接口[因为继承过来就相当于成为了派生了的内置实用函数了...],派生类在实现中被继承类但继承基类的部分并未成为其接口的一部分!【实现继承】

[迄今为止:最常见的继承形式是public!]



【关键概念:继承与组合】

定义一个类作为另一个类的公用派生类时,派生类应反映与基类的“是一种(IsA)”关系。在书店例子中,基类表示按规定价格销售的书的概念,Bulk_item是一种书,但具有不同的定价策略。

类型之间另一种常见的关系是称为“有一个(HasA)”的关系。书店例子中的类具有价格和ISBN。通过“有一个”关系而相关的类型暗含有成员关系,因此,书店例子中的类由表示价格和ISBN的成员组成。



2、去除个别成员

如果进行private/protected继承,则基类成员的访问级别在派生类中比在基类中更受限:

class Base
{
public:
    std::size_t size() const
    {
        return n;
    }

protected:
    std::size_t n;
};

class Derived : private Base
{
    //...
};

//测试
int main()
{
    Derived de;
    std::size_t n = de.size();  //Error
}

【注解】

派生类可以恢复继承成员的访问级别,但不能使得访问级别比基类中原来指定的更严格( )或更宽松!

在上例中,size在 Derived中为private。为了使size在 Derived中恢复往日的地位[public],可以在Derived的public部分增加一个using声明。如下这样改变Derived的定义,可以使size成员能够被用户访问,并使n能够被从Derived派生的类访问:

class Derived : private Base
{
public:
    using Base::size;

protected:
    using Base::n;
};

正如可以使用using声明从命名空间使用名字,也可以使用using声明访问基类中的名字

此时:

    Derived de;
    std::size_t n = de.size();  //OK

3、默认继承保护级别

默认继承访问级别根据使用哪个保留关键字定义派生类也不相同:使用class定义的派生类默认具有private继承,而struct定义的类默认具有public继承:

class Base
{
    /* ... */
};
struct D1 : Base    //struct D1 : public Base
{
    /* ... */
};
class D2 : Base     //class D2 : private Base
{
    /* ... */
};

注意:class与struct默认继承的唯一区别只是默认的成员保护级别和默认的派生保护级别!

class D3 : public Base
{
public:
    /* ... */
};
// equivalent definition of D3
struct D3 : Base
{
    /* ... */
};

struct D4 : private Base
{
private:
    /* ... */
};
// equivalent definition of D4
class D4 : Base
{
    /* ... */
};

【最佳实践】

尽管私有继承在使用class保留字时是默认情况,但这在实践中相对罕见[所以建议最好不要使用,因为最终阅读你的源码的不只是计算机,还有程序员!]。因为私有继承是如此罕见,通常显式指定 private是比依赖于默认更好的办法。显式指定可清楚指出想要私有继承而不是一时疏忽。



六、友元关系与继承

友元关系不能继承。基类的友元派生类的成员没有特殊的访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。

每个类控制对自己的成员的友元关系:

class Base
{
    friend class Frnd;

protected:
    int i;
};

//Frnd对D1没有特殊的访问权限
class D1 : public Base
{
protected:
    int j;
};

class Frnd
{
public:
    int mem(const Base &obj)
    {
        return obj.i;   //OK
    }
    int mem(const D1 &obj)
    {
        return obj.j;   //Error
    }
};
//D2对Base没有特殊的访问权限
class D2 : public Frnd
{
public:
    int mem(const Base &obj)
    {
        return obj.i;   //Error
    }
};

基类的友元对从该基类派生的类型没有特殊访问权限,同样,如果基类和派生类都需要访问一个类,那个类必须特定的将访问权限授予基类和每一个派生类。



七、继承与静态成员

如果基类定义了static成员,则整个继承层次只有一个这样的成员:无论从基类派生出多少个派生类,每个static成员只有一个实例

static成员遵循常规访问控制:如果成员在基类中为private,则派生类不能访问它。假定可以访问该static成员[如public],则既可以通过基类访问static成员,也可以通过派生类访问static成员。一般而言,既可以使用作用域操作符也可以使用点或箭头成员访问操作符。

struct Base
{
    static void statMem();
};

struct Drived : public Base
{
    void f (const Drived &);
};

void Drived::f(const Drived &derived_obj)
{
    Base::statMem();
    Drived::statMem();
    derived_obj.statMem();
    statMem();
}


//P487 习题15.13
struct ConcreteBase
{
    static std::size_t object_count();

protected:
    static std::size_t obj_count;
};

struct C1 : public ConcreteBase
{
    void f(const