1.多态
(1)多态性是面向对象程序设计的重要特征之一。
(2)多态性是指发出相同的消息被不同类型的对象接收时,有可能导致完全不同的行为。
(3)多态的实现:函数重载,运算符重载,模板,虚函数
2.静态绑定与动态绑定
(1)静态绑定:绑定过程出现在编译阶段,在编译期就已确定要调用的函数。
(2)动态绑定:绑定过程工作在程序运行时执行,在程序运行时才确定将要调用的函数。
3.虚函数
(1)虚函数的概念:在基类中冠以关键字virtual的成员函数
(2)虚函数的定义:
[1]virtual 函数类型 函数名称(参数列表)
[2]如果一个函数在基类中被声明为虚函数,则它在所有派生类中都是虚函数
(3)只有通过基类指针或引用调用虚函数才能引发动态绑定。
(4)虚函数不能声明为静态
4.虚析构函数
(1)何时需要虚析构函数?
(2)当你可能通过基类指针删除派生类对象是
(3)如果你算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),并且被析构的对象是有重要的析构函数的派生类的对象,就需要让基类的析构函数作为虚函数
5.虚表指针
(1)虚函数的动态绑定是通过虚表来实现的
(2)包含虚函数的类头4个字节存放指向虚表的指针
32 位系统虚拟内存空间的分布情况,最上方的内核空间不用多讲,下方的用户空间内存
通过这张图你可以看到,用户空间内存,从低到高分别是五种不同的内存段。
-
只读段,包括代码和常量等。
-
数据段,包括全局变量等。
-
堆,包括动态分配的内存,从低地址开始向上增长。
-
文件映射段,包括动态库、共享内存等,从高地址开始向下增长。
-
栈,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是 8 MB。
#include <iostream>
using namespace std;
class Base {
public:
virtual void Fun1() {
cout << "Base::Fun1" << endl;
}
virtual void Fun2() {
cout << "Base::Fun2" << endl;
}
int data1_;
};
class Derived : public Base {
public:
virtual void Fun2() override final {
cout << "Derived::Fun2" << endl;
}
virtual void Fun3() {
cout << "Derived::Fun3" << endl;
}
int data2_;
};
typedef void (*FUNC)();
int main() {
cout << sizeof(Base) << endl;
cout << sizeof(Derived) << endl;
Base b;
long**p = (long**)&b;
FUNC fun = (FUNC)p[0][0];
(*fun)();
fun = (FUNC)p[0][1];
(*fun)();
cout << endl;
Derived d;
p = (long**)&d;
fun = (FUNC)p[0][0];
(*fun)();
fun = (FUNC)p[0][1];
(*fun)();
fun = (FUNC)p[0][2];
(*fun)();
cout << endl;
Base* pp = &d;
pp->Fun2(); // 动态绑定
d.Fun2(); // 静态绑定
return 0;
}
6.object slicing与虚函数
模板方法模式
#include <iostream>
using namespace std;
class CObject {
public:
virtual void Serialize() {
cout << "CObject::Serialize ..." << endl;
}
};
class CDocument : public CObject {
public:
int data1_;
CDocument() {
cout << "CDocument()" << endl;
}
CDocument(const CDocument& other) {
cout << "CDocument(const CDocument& other)" << endl;
}
void func() {
cout << "CDocument::func ..." << endl;
Serialize();
}
virtual void Serialize() override {
cout << "CDocument::Serialize ..." << endl;
}
};
class CMyDoc : public CDocument {
public:
int data2_;
virtual void Serialize() override {
cout << "CMyDoc::Serialize ..." << endl;
}
};
int main() {
CMyDoc mydoc;
CMyDoc* pmydoc = new CMyDoc;
cout << "#1 testing" << endl;
mydoc.func();
cout << "#2 testing" << endl;
((CDocument*)(&mydoc))->func();
cout << "#3 testing" << endl;
pmydoc->func();
cout << "#4 testing" << endl;
((CDocument)mydoc).func(); // mydoc对象强制转换为CDocument对象,向上转型
//完完全全将对象转化为基类对象
//调用默认的拷贝构造函数
delete pmydoc;
pmydoc = NULL;
return 0;
}
7.overload,override,overwrite
(1)成员函数被重载的特征:overload
[1]相同的范围(在同一个类中);
[2]函数名字相同;
[3]参数不同;
[4]virtual关键字可有可无
(2)覆盖是指派生类函数覆盖基类函数,特征是:override
[1]不同的外围(分别位于派生类与基类);
[2]函数名字相同;
[3]参数相同;
[4]基类函数必须有virtual关键字
(3)重定义(派生类与基类):overwrite
[1]不同的范围(分别位于派生类与基类);
[2]函数名与参数都相同,无virtual关键字;
[3]函数名相同,参数不同,virtual可有可无
1. 子类继承父类的虚函数表的方式是复制一份。存在虚函数的类,都有自己的虚函数表,不与其他类共用。
2. 只要祖先类的某个函数被声明位virtual, 则在后代中无论是否显式地添加virtual,该函数一直都是虚的。
3. 如果子类重写了某个虚函数,则该类的虚函数表对应位置上的虚函数地址会被覆盖。如果完全不重写,则子类的虚函数表里面的内容和父类虚函数表内容保持一致。
4. 虚函数表类似于类的静态成员,被该类的所有实例共享。只不过该表是只读的,不存在线程安全问题。
5. 实例对象在起始4字节中,会存放虚函数表的地址(跟系统寻址范围有关,有的系统8字节才能表示一个地址),可以通过手动寻址的方式完成虚函数调用。
6. 多重继承(有多个父类并列)时,该类有多张虚函数表,0~3字节,4~7字节分别表示第一张虚函数表地址,第二张虚函数表地址,以此类推。
7. 即便父类的虚函数是private的,子类的虚函数表中也依然存在它的地址,我们可以通过子类实例的首地址,寻找到虚函数表进而找到该虚函数,完成调用。这样会违背c++语言的设计初衷,破坏数据的安全性,一般不要轻易使用。
#include <iostream>
#include <string>
using namespace std;
typedef void(__stdcall *Fun)();
typedef void(__stdcall *Fun1)(double);
//声明virtual,让该成员函数地址在虚函数表中留下记录
class Brass{
private:
std::string fullname;
long acctNum;
double balance;
public:
Brass(const std::string & s ="NullBody",long an=-1,double bal=0.0);
void Deposit(double amt);
virtual void ViewAcct() const;
virtual void withdraw(double amt);
double Balance() const;
virtual void test(){cout<<"Brass::test"<<endl;}
virtual ~Brass(){}
};
//即便子类不再将父类的虚函数函数声明为virtual,只要祖先类中该函数是virtual,则它依然是虚函数
class BrassPlus:public Brass{
private:
double maxLoan;
double rate;
double owesBank;
public:
BrassPlus(const std::string & s ="NullBody",long an=-1,
double bal=0.0,double ml=500,double r=0.11125);
BrassPlus(const Brass &ba,double ml=500,double r=0.11125);
void ViewAcct()const;
void withdraw(double amt);
void ResetMax(double m){maxLoan=m;}
void ResetRate(double r){rate = r;}
void ResetOwes(){owesBank=0;}
};
class BrassPlusPlus:public BrassPlus{
public:
void ViewAcct() const override{cout<<"BrassPlusPlus viewacct"<<endl;}
};
Brass::Brass(const std::string & s,long an,double bal):fullname(s),acctNum(an),balance(bal){}
void Brass::Deposit(double amt){return;}
void Brass::withdraw(double amt){cout<<"Brass withdraw: "<<amt<<endl;return;}
double Brass::Balance() const{return balance;}
void Brass::ViewAcct()const{cout<<"Brass viewacct"<<endl;}
BrassPlus::BrassPlus(const std::string & s,long an,double bal,double ml,double r):Brass(s,an,bal),maxLoan(ml),rate(r){}
BrassPlus::BrassPlus(const Brass &ba,double ml,double r):Brass(ba),maxLoan(ml),rate(r){}
void BrassPlus::ViewAcct()const{cout<<"BrassPlus viewacct"<<endl;}
void BrassPlus::withdraw(double amt){cout<<"BrassPlus withdraw: "<<amt<<endl;return;}
void test(const Brass& br){
br.ViewAcct();
//br.withdraw(6.0);//编译不通,const 实例只能调用const成员函数;
Brass& bbp=const_cast<Brass&>(br);
bbp.withdraw(6.0);
}
int main()
{
#ifdef _WIN64
typedef __int64 ADTY;//64位操作系统,8个字节表示一个地址
#else
typedef __int32 ADTY;
#endif
cout << "Hello World!" << endl;
Brass bra("Jim",2,0.06);
BrassPlus brap("Jhon",5,0.58,500,0.014);
const Brass bra_2("Jack",3,0.02);
Brass& b1 = bra;
Brass& b2 = brap;
Brass *bas[2];
bas[0] = &bra;
bas[1] = &brap;
cout << "对象bas[0]的虚函数表位于:"<<hex<<*(ADTY*)bas[0]<<",对象bas[0]的第一个虚函数位于:"<<hex<<*( (ADTY *)*(ADTY*)bas[0] + 0 )<<endl;
cout << "对象bas[1]的虚函数表位于:" <<hex<<*(ADTY*)bas[1]<<",对象bas[1]的第一个虚函数位于:"<<hex<<*( (ADTY *)*(ADTY*)bas[1] + 0 )<<endl;
cout << "子类虚函数表会从父类继承(复制一份,不是共用一份),如果子类重写了某个虚函数,则子类的虚函数表中对应的函数地址会被覆盖"<<endl;
Fun cc = (Fun)( *( (ADTY *)*(ADTY*)bas[1] + 0 ) );
Fun1 dd = (Fun1)( *( (ADTY *)*(ADTY*)bas[1] + 1 ) );
cout<<"对象bas[1]虚函数调用结果:"<<endl;
cc();
dd(8.9);
ADTY *ptr = (ADTY*)(&bra);
//可以看出虚函数表类似于类的静态成员,对象的首地址位置保存了虚函数表的地址
cout << "对象bra的虚函数表位于:"<<hex<<*ptr<<",对象bra的第一个虚函数位于:"<<hex<<*( (ADTY*)*ptr + 0 )<<endl;
cout << "对象bra_2的虚函数表位于:" <<hex<<*(ADTY*)(&bra_2)<<",对象bra_2的第一个虚函数位于:"<<hex<<*( (ADTY*)*(ADTY*)(&bra_2) + 0 )<<endl;
cout << "说明bra的虚函数表 与 bra_2的虚函数表 是同一张表"<<endl;
ADTY* ptr1 =(ADTY*)*ptr;
Fun pFun = (Fun)*(ptr1+0); //等效于 (Fun)(*((ADTY*)*(ADTY*)(&bra)+0));
pFun();
Fun1 pFun1 = (Fun1)*(ptr1+1); //等效于 (Fun)(*((ADTY*)*(ADTY*)(&bra)+1));
pFun1(9.0);
cout << "Hello World end" << endl;
return 0;
}