1. 派生类的作用域
每个类定义自己的作用域,作用域内定义成员,当存在继承关系时,派生类的作用域嵌套在其基类的作用域之内。如果一个名字在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义。
Bulk_quote bulk;
cout << bulk.isbn();
. 首先在Bulk_quote 中查找名字isbn.
. 在Bulk_quote的基类Disc_quote中查找.
. 在Disc_quote的基类Quote中查找.
1.1 在编译时进行名字查找
一个对象、引用或指针的静态类型决定了该对象的哪些成员可见,即使动态类型和静态类型不一致。
namespace oopClassDomainTest
{
class Quote {
};
class Disc_quote : public Quote {
public:
Disc_quote() {};
void discount_policy() {
}
};
class Bulk_quote : public Disc_quote {
};
void test() {
Bulk_quote bulk;
Bulk_quote* bulkP = &bulk;//静态类型和动态类型一致
Quote* itemP = &bulk;//静态类型和动态类型不一致
bulkP->discount_policy(); //ok
itemP->discount_policy();//error
}
}
1.2. 名字冲突与继承
派生类可以重定义基类成员,从而将其隐藏。但可以通过作用域运算符来使用隐藏的成员
struct Base {
Base() : mem(0) { }
protected:
int mem;
};
struct Derived : Base {
Derived(int i) : mem(i) { } // initializes Derived::mem to i
// Base::mem is default initialized
int get_mem() { return mem; } // returns Derived::mem
int get_base_mem() { return Base::mem; }
// . . .
protected:
int mem; // hides mem in the base
};
void test() {
Derived d(42);
cout << d.get_mem() << endl; // prints 42
cout << d.get_base_mem() << endl; // prints 0
}
1.3 名字查找优先于类型检查
声明在内层作用域的函数并不会重载声明在外层作用域的函数,如果派生类中的函数与基类的某个成员同名,则派生类将在其作用域内隐藏该基类成员,即使形参列表不一致。
namespace oopClassDomainTest3
{
struct Base {
int memfcn() { return 1; };
};
struct Derived : Base {
int memfcn(int) { return 1; };// hides mem in the base
};
void test() {
Derived d;
Base b;
b.memfcn();
d.memfcn(10);
//d.memfcn();//error
d.Base::memfcn();//ok
}
}
1.4 虚函数和作用域
不论静态类型,调用的是虚函数结果就是虚函数。
class Base {
public:
virtual int fcn();
};
class D1 : public Base {
public:
// hides fcn in the base; this fcn is not virtual
// D1 inherits the definition of Base::fcn()
int fcn(int); // parameter list differs from fcn in Base
virtual void f2(); // new virtual function that does not exist in Base
};
class D2 final : public D1 {
public:
int fcn(int); // nonvirtual function hides D1::fcn(int)
int fcn(); // overrides virtual fcn from Base
void f2(); // overrides virtual f2 from D1
};
D1 dobj, * dp = &dobj;
dp->fcn(42); // ok: static call to D1::fcn(int)
Base bobj; D1 d1obj; D2 d2obj;
Base* bp1 = &bobj, * bp2 = &d1obj, * bp3 = &d2obj;
bp1->fcn(); // 虚调用, Base::fcn
bp2->fcn(); // 虚调用, Base::fcn at run time
bp3->fcn(); // 虚调用, D2::fcn at run time
D1* d1p = &d1obj; D2* d2p = &d2obj;
d1p->f2(); // 虚调用, will call D1::f2() at run time
d2p->f2(); // 虚调用, will call D2::f2() at run time
D1* p2 = &d2obj; D2* p3 = &d2obj;
p2->fcn(42); // statically bound, calls D1::fcn(int)
p3->fcn(42); // statically bound, calls D2::fcn(int)
D1* dp1 = &d2obj; D2* dp2 = &d2obj;
dp1->fcn(10); // static call to D1::fcn(int)
dp2->fcn(10); // static call to D2::fcn(int)
1.5 覆盖重载函数
应用场景:基类中可能有多个重载函数,派生类希望覆盖重载函数中的一部分。
解决方法:使用using声明语句指定一个名字而不指定形参列表,一条基类成员函数的using声明语句就可以把该函数所有重载实例添加到派生类作用域。 之后派生类仅需对特定参数的函数定义,无需为其他继承而来的函数定义。
声明在内层作用域的函数并不会重载外层作用域的函数,因此定义派生类的函数也不会重载其基类中的成员。如果同名则外层的名字隐藏。
【引用】
[1] 代码oopTest.h