面向对象编程基于三个基本概念:
面向对象编程的关键思想是多态性(polymorphism)
。多态性派生于一个希腊单词,意思是“许多形态”,之所以称通过继承而相关联的类型为多态类型,是因为在许多情况下可以互换地使用派生类型或基类型的“许多形态”。在C++中,多态性仅用于通过继承而相关联的类型的引用或指针。
在C++中,基类必须指出希望派生类重写哪些函数,定义为virtual
的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数。
在C++中,通过基类的引用(或指针)调用虚函数时,发生动态绑定
。引用(或指针)既可以指向基类对象也可以指向派生类对象
,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型所定义的。
引用和指针的静态类型与动态类型可以不同,这是 C++ 用以支持多态性的基石。
virtual
的目的是启用动态绑定。成员默认为非虚函数
,对非虚函数的调用在编译时确定。除了构造函数之外,任意非static成员函数都可以是虚函数
。类内部的成员函数声明中出现,不能用在类定义体外部出现的函数定义上。
访问控制
派生类对基类的public和private成员的访问权限
与程序中任意其他部分一样:它可以访问public成员
而不能访问private成员
。
有时作为基类的类具有一些成员,它希望允许派生类访问但仍禁止其他用户访问这些成员。对于这样的成员应使用受保护的访问标号。protected成员可以被派生类对象访问但不能被该类型的普通用户访问。
protected
成员和public
成员的组合class
classname: access-label base-class
虚函数的声明必须与基类中的定义方式完全匹配
,
但有一个例外:返回对基类型的引用(或指针)的虚函数。派生类中的虚函数可以返回基类函数所返回类型的派生类的引用(或指针)。
虚函数的成员函数才能进行动态绑定
基类类型的引用或指针进行函数调用
编译器都将它当作基类类型对象
。静态类型
(在编译时可知的引用类型或指针类型)和动态类型
(指针或引用所绑定的对象的类型这是仅在运行时可知的)可能不同。覆盖虚函数机制
虚函数与默认实参
编译时确定
。类型定义,与对象的动态类型无关
。通过基类的引用或指针调用虚函数时,默认实参为在基类虚函数声明中指定的值,如果通过派生类的指针或引用调用虚函数,则默认实参是在派生类的版本中声明的值
。
为什么会希望覆盖虚函数机制?
最常见的理由是为了派生类虚函数调用基类中的版本。在这种情况下,基类版本可以完成继承层次中所有类型的公共任务,而每个派生类型只添加自己的特殊工作。
例如,可以定义一个具有虚操作的Camera类层次。Camera类中的display函数可以显示所有的公共信息,派生类(如PerspectiveCamera)可能既需要显示公共信息又需要显示自己的独特信息。可以显式调用Camera版本以显示公共信息,而不是在PerspectiveCamera的display实现中复制Camera的操作。 在这种情况下,已经确切知道调用哪个实例,因此,不需要通过虚函数机制。
共同控制
。派生类可以进一步限制但不能放松对所继承的成员的访问
。公用继承
,基类的public成员为派生类的public成员,基类的protected成员为派生类的protected成员。受保护继承
,基类的public和protected成员在派生类中为protected成员。私有继承
,基类的的所有成员在派生类中为private成员。在派生类的成员函数内部访问基类的成员,访问级别取决于基类的类型(可访问public/private)
[Code4]在派生类定义与实现的外部,即派生类的对象或者继承自派生类的类,派生类成员访问级别取决于派生类继承基类的访问标号
[Code5]class
保留字定义的派生默认具有private继承
,而用struct
保留字定义的类默认具有public继承
。友元关系不能继承
。基类的友元对派生类的成员没有特殊访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。一个实例
。作用域操作符
也可以使用点或箭头成员访问操作符
。/*假定 Bulk_item 定义了一个成员函数,接受一个 Bulk_item 对象的 引用和一个 Item_base 对象的引用,该函数可以访问自己对象的 protected 成 员以及 Bulk_item 形参的 protected 成员,但是,它不能访问 Item_base 形 参的 protected 成员。*/ void Bulk_item::memfcn(const Bulk_item &d, const Item_base &b) { // attempt to use protected member double ret = price; // ok: uses this->price ret = d.price; // ok: uses price from a Bulk_item object ret = b.price; // error: no access to price from an Item_base } /*d.price 的使用正确,因为是通过 Bulk_item 类型对象引用 price;b.price 的 使用非法,因为对 Base_item 类型的对象没有特殊访问访问权限。*/
//error: a forward declaration must not include the derivation list class Bulk_item : public Item_base; //正确的前向声明为: //forward declarations of both derived and nonderived class class Bulk_item; class Item_base;
Item_base *baseP = &derived; //calls version from the base class regardless of the dynamic type of baseP double d = baseP->Item_base::net_price(42);
class Base { public: void basemem(); protected: int i; // ... }; struct Public_derived : public Base { int use_base() { return i; } // ok: derived classes can access i // ... }; struct Private_derived : private Base { int use_base() { return i; } // ok: derived classes can access i };
Base b; Public_derived d1; Private_derived d2; b.basemem(); // ok: basemem is public d1.basemem(); // ok: basemem is public in the derived class d2.basemem(); // error: basemem is private in the derived class //派生访问标号还控制来自非直接派生类的访问: struct Derived_from Private : public Private_derived { // error: Base::i is private in Private_derived int use_base() { return i; } }; struct Derived_from_Public : public Public_derived { // ok: Base::i remains protected in Public_derived int use_base() { return i; } };
class Base { public: std::size_t size() const { return n; } protected: std::size_t n; }; class Derived : private Base { . . . }; /*在这一继承层次中,size 在 Base 中为 public,但在 Derived 中为 private。为了使 size 在 Derived 中成为 public,可以在 Derived 的 public 部分增加一个 using 声明。如下这样改变 Derived 的定义,可以使 size 成员 能够被用户访问,并使 n 能够被从 Derived 派生的类访问:*/ class Derived : private Base { public: // maintain access levels for members related to the size of the object using Base::size; protected: using Base::n; // ... };
原文:http://blog.csdn.net/liufei_learning/article/details/21436035