C++面向对象程序设计的核心思想是数据抽象、继承和动态绑定。
C++中的成员函数分为两种,一种为虚函数,一种为非虚函数,虚函数是在运行时解析的,非虚函数是在编译时解析的。
2.派生类以及派生类向基类的类型
Quote item; Bulk_quote bulk; Quote *p = &item; //p 指向Quote 对象 p = &bulk; // p 指向bulk的Quote 部分 Quote &r = bulk; // r 绑定到bulk的Quote 部分
派生类的构造函数
派生类也必须使用基类的构造函数来初始化它的基类部分。
每个类负责定义各自的接口,因此派生类不能直接初始化基类的成员。
无论从基类中派生出多少个派生类,每个静态成员都只有一个。
class Base; class D1:public Base class D2:public D1
在继承关系中,Base 是D1的直接基类,同时是D2的间接基类。要防止继承的发生。
防止继承
class NoDerived final //不能作为基类 class Base class Last final :Base class Bad :NoDerved //错误 class Bad2:Last //last 是final 的
3.静态类型与动态类型
表达式的静态类型在编译时是已经知道的,动态类型则是变量或者表达式表示的内存中的对象的类型。
如果表达式既不是引用也不是指针,则它的动态类型永远和静态类型一致。
转换方式:
1、只能由派生类转换为基类。
2、基类不能转换诶派生类。
3、派生类向基类的自动类型转换只对指针或者引用有效。
4 虚函数
1、对虚函数的调用只有在运行的时候才能被解析。
2、被调用的函数是与绑定到指针或引用上的动态类型匹配的那一个,请注意,是指针或者引用在调用时才知道,通过对象调用的函数,在编译的时刻就确定了。
3、对非虚函数的调用是在编译时绑定的,通过对象进行的函数调用也是在编译的时候调用的
4、派生类中的虚函数,因为一旦某个函数被声明为虚函数了,则在派生类中它都是虚函数。比如说在基类中定义了为virtual, 在派生类中没有定义为virtual,实际上没有什么影响,只是为了好看而已。
5、正常情况下,派生类中虚函数的参数必须与基类中虚函数的参数一致,但是除了虚函数的返回类型是类本身的指针或者引用时的这种情况。
派生类向基类的可访问性这块儿还是没有看明白;(544页)
基类的可访问性
回避虚函数的机制
double undisconted = basep->Quote::net_price(42)
这样就会强行调用基类的net_price函数,不管base指向的是什么内容。
抽象基类
纯虚函数
访问控制与继承
友元和继承
友元关系不能传递,同样也不能继承,基类的友元,在访问派生类成员时不具有特殊性,类似,派生类的友元也不能随意访问基类的成员。
class Base { protected: int prot_mem; }; class Sneaky:public Base { friend void clobber(Sneaky &); // sneakey::prot_mem friend void clobber(Base &); // base::prot_mem int j; // }; void clobber(Sneaky &s){ s.j = s.prot_mem = 0; } void clobber(Base &b) { b.prot_mem; } **:派生类的成员或友元只能通过派生类对象来访问基类的受保护成员,派生类对于一个基类对象中的受保护成员没有任何访问特权。
继承中的作用域
友元与继承
友元关系不能传递,同样也不能继承,
class Base
{
protected:
int prot_mem;
friend class Pal; //重要,pal在访问Base的派生类的时候不具有特殊性。
};
class Sneaky:public Base
{
friend void clobber(Sneaky &); // sneakey::prot_mem
friend void clobber(Base &); // base::prot_mem
int j; //
};
class Pal { public : int f(Base b){ return b.prot_mem;} int f2(Sneaky s) {return s.j;}; //不能这么写,因为 Pal 不是 Sneaky的友元。 int f3(Sneaky s) {return s.prot_mem;};//OK, 因为 对基类的访问权限是由基类本身控制的,对于派生类中的基数部分也是如此。 };
f3 的访问就是说明 Pal是Base的友元,所以Pal能够访问 Base对象的成员。
class D2 :public Pal { public : int mem(Baseb b) { return b.prot_mem; } }; //不可以,因为友元函数不能继承。 总结一点,当一个类将另外一个类声明为友元时,这种友元关系只对做出声明的类有效果。
继承中的类作用域
派生类的作用域位于基类的作用域之内,如果一个名字在派生类的作用域里面无法解析的时候,编译器会在外层的基类的作用域里面寻找该名字的定义。
Bulk_quote bulk;
cout<<bult.isbn();
首先在派生类的作用域里面找,如果派生类的作用域里面没有,再在它的基类的作用域里面找,基类没有,再在上一层的基类的作用域里面找。名字的查找是在编译的过程中实现的。
基类与派生类名字的关系
struct Base { Base():mem(){} protected: int mem; }; struct Derived:Base{ Derived(int i) :mem(i){} int get_mem() {retruen mem;} protected: int mem; //这个将隐藏基类中的mem变量 };
不过想要使用基类中的变量,可以用作用域来实现的,如:
struct Derived:Base{
int get_base_mem() {return Base::mem;}
}; 这样是可以的。
当派生类成员和基类成员的形参不一致时,基类的成员也会被隐藏掉。
struct Base{ int memfcn(); }; struct Derived:Base{ int memfcn(int); }; Derived d;Base b; b.memfcn(); //ok Base::memfcn(); d.memfcn(10); //ok Derived::memfcn(int); d.memfcn(); //ERROR b.Base::memfcn(); //OK ERROR的是因为参数为空的memfcn被隐藏了。