C++ primer 摘要《类继承、基类派生类、虚拟函数》20090202

===17.1 定义一个类层次结构===
在派生表中指定的类必须首先被定义好,方可被指定为基类
// 错误: Query 必须已经被定义
class Query;
class NameQuery : public Query { ... };
===17.3 基类成员访问===
1、类域、可视性
class Diffident {
public:
    void turn_aside();
    // ...
};
class Shy : public Diffident {
public:
    // 隐藏了 Diffident::turn_aside() 的可视性
    void turn_aside();
    // ...
};
2、如果我们真的希望为基类和派生类的成员实例提供一个重载函数集合,该怎么办呢?
class Shy : public Diffident {
public:
    // ok: 方法之一: 为基类和派生类的成员
    // 提供一个重载函数集合
    void mumble( string whatYaSay );
    void mumble( int softness ) {
        Diffident::mumble( softness );
    }
    // ...
};
C++ 下通过 using 声明实现
class Shy : public Diffident {
public:
    // ok: 在标准 C++ 下通过 using 声明
    // 创建了基类和派生类成员的重载集合
    void mumble( string whatYaSay );
    using Diffident::mumble;
    // ...
};                      
using 声明不能指定参数表,只能指定成员函数名,这意味着如果该函数在基类中被重载,
则所有的重载实例都被加入到派生类类型的域中
3、protected成员访问
class Query {
public:
    const vector<location>* locations() const { return &_loc; }
    // ...
protected:
    vector<location> _loc;
    // ...
};
bool NameQuery::compare( const Query *pquery )//NameQuery是Query的派生类
{
    // ok: 自己的 Query 子对象的 protected 成员
    int myMatches = _loc.size();
    // 错误: 没有 "直接访问另一个独立的 Query
    // 对象的 protected 成员" 的权利
    int itsMatches = pquery->_loc.size();
    return myMatches == itsMatches;
}
注意:派生类可以直接访问该类其他对象的protected 基类成员,以及该类其他对象的protected和private 成员
bool NameQuery::compare( const NameQuery *pname )
{
    int myMatches = _loc.size(); // ok
    int itsMatches = pname->_loc.size(); // ok as well 这个需要注意
    return myMatches == itsMatches;
}
4、静态成员
Query 基类定义了一个静态数据成员,则所有派生类对象都引用这个相同的单一的共享的静态成员
5、友元关系不会被继承
如继承关系Query<-NameQuery<-StringQuery
如果NameQuery是Query的友元,StringQuery并不会因此而成为Query的友元
===17.4 基类和派生类的构造===
构造函数的调用顺序
1 基类构造函数,如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在成员初始化表中的顺序
2 成员类对象构造函数,如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序//这个需要注意,也是自动调用的
3 派生类构造函数
===17.4.3 另外一个类层次结构===
派生类构造函数只能合法地调用其直接基类的构造函数
例如AndQuery 在其成员初始化表中调用Query 构造函数就是错误的
(Query<-BinaryQuery<-AndQuery)
===17.5.1 虚拟的输入输出===
1、定义:关键字 virtual 只能出现在类定义中(就是函数声明的时候)
第一次引入虚拟函数的基类时,必须在类声明中指定virtual 关键字,如果定义被放在类的外面则不能再次指定关键字virtual
2、为了使虚拟函数的派生类实例能够改写其基类的活动实例,它的原型必须与基类完全匹配(函数名、参数、返回值)
但对于返回值有一个特例,派生类实例的返回值可以是基类实例返回类型的公有派生类类型,例如,如果基类实例返回一个Query* 则派生类实例可以返回NameQuery*
===17.5.3 虚拟函数的静态调用===
纯虚函数可以被定义、可以被静态调用
class Base
{
public:
 virtual void print()=0;//必须要有virtual
};
class Drive :public Base
{
public:
 virtual void print();//virtual可以不需要
};
void Base::print()//定义纯虚函数,不能含有virtual
{
 cout<<"Base"<<endl;
}
void Drive::print()//不能含有virtual
{
 cout<<"Drive"<<endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
 Base *p = new Drive();
 p->print(); //打印Drive
 p->Base::print();//静态调用 打印Base

 return 0;
}
===17.5.4 虚拟函数和缺省实参===
缺省实参不是在运行时刻决定的,而是在编译时刻根据被调用函数的对象的类型决定的
class base {
public:
    virtual int foo( int ival = 1024 ) {
    cout <<"base::foo() -- ival: " << ival << endl;
    return ival;
    }
    // ...
};
class derived : public base {
public:
    virtual int foo( int ival = 2048 ) {
    cout << "derived::foo() -- ival: " << ival << endl;
    return ival;
    }
    // ...
};
derived *pd = new derived();
base *pb = pd;
int val = pb->foo(); //缺省实参为1024
val = pd->foo(); //缺省实参为2048
如果我们确实希望传递给foo()的实际缺省实参是根据被调用函数的实际实例而决
定的,那么该怎么办呢?不幸的是虚拟机制不直接支持这种行为
解决办法:把缺省实参放入函数体
void base::foo( int ival = base_default_value )
{
    int real_default_value = 1024;
    if ( ival == base_default_value )//如果用户没有传递相应的值
        ival = real_default_value;
    // ...
}
void derived::foo( int ival = base_default_value )
{
    int real_default_value = 2048;
    if ( ival == base_default_value )//如果用户没有传递相应的值
        ival = real_default_value;
    // ...
}
===17.5.5 虚拟析构函数===
1、访问级别
class Query {
public: // ...
protected://保护级别
    virtual ~Query();
    // ...
};
class NotQuery : public Query {
public://公共级别
    ~NotQuery();
    // ...
};
当通过NotQuery 对象调用NotQuery 析构函数时,它的访问级别是public ,但是当通过Query 指针或引用来调用析构函数时,它是protected ,
即虚拟函数承接了调用者所属类类型的访问级别
因此
int main()
{
    Query *pq = new NotQuery;
    // 非法: 析构函数是 protected
    delete pq;
}
一般地,基类的析构函数不应该是protected
===17.5.7 虚拟new 操作符===
new 操作符不能被声明为虚拟的,因为它是一个静态成员函数
在构造类对象之前被应用到未使用的内存上
问题;
已知一个指针指向一个实体查询子类型,那么在堆中分配一个复制的对象就是一件很容易的事情
NotQuery *pnq;
// set pnq ...
// new 表达式调用 NotQuery 的拷贝构造函数
NotQuery *pnq2 = new NotQuery( *pnq );
但是,如果已知一个指向抽象Query 的指针(注意不是指向实体查询子类型),不知道它实际指向的类型,该如何复制?
const Query *pq = pnq->op();
// 怎样复制 pq?
解决办法:
如果能声明一个操作符new 的虚拟实例则问题就解决了,但new 操作符不能被声明为虚拟的,因为它是一个静态成员函数
使用代理"new 操作符"--clone()
class NameQuery : public Query {
public:
    virtual Query *clone() //注意返回类型为Query*
    // 调用 NameQuery 的拷贝构造函数
    { return new NameQuery( *this ); }
    // ...
};
Query *pq = new NameQuery( "Valery" );
Query *pq2 = pq->clone();
NameQuery *pnq = new NameQuery( "Rilke" );
NameQuery *pnq2 = static_cast<NameQuery*>( pnq->clone() );
改进:
class NameQuery : public Query {
public:
    virtual NameQuery *clone() //注意返回类型为NameQuery*,这种也是支持虚拟的,见17.5.1 虚拟的输入输出中的特例
    { return new NameQuery( *this ); }
    // ...
};
Query *pq = new NameQuery( "Valery" );
Query *pq2 = pq->clone();
NameQuery *pnq = new NameQuery( "Rilke" );
NameQuery *pnq2 = pnq->clone();
为了这些clone()的实现能够成功,我们必须提供显式的AndQuery的拷贝构造函数实例
===17.5.8 虚拟函数构造函数和析构函数===
如果在基类的构造函数中调用了一个虚拟函数,而基类和派生类都定义了该函数的实例,将会怎么样?==>调用基类的虚拟函数
  在基类构造函数中调用的虚拟实例总是在基类中活动的虚拟实例,不会是派生类的
对于派生类对象,在基类析构函数中也是如此

 

你可能感兴趣的:(C++,String,活动,delete,query,Class)