1.类别及其成员- 谈封装(encapsulation)
member variable(成员变量)和member function(成员函数)
2.基础类别与衍生类别:谈继承(Inheritance)
1.子类拥有父类的所有属性和动作。
2.不同的子类可以继承父类一个相同的动作。
3. 既然所有类别都有display 动作,把它提升到老祖宗CShape 去,然后再继承
之,好吗?不好,因为display 函数应该因不同的形状而动作不同。
4. 如果display 不能提升到基础类别去,我们就不能够以一个for 循环或while
循环干净漂亮地完成下列动作(此种动作模式在对象导向程序方法中重要无
比):
CShape shapes[5];
// 令5 个shapes 各为矩形、四方形、椭圆形、圆形、三角形
for (int i=0; i<5; i++)
{
shapes[i].display;
}
3.this 指针
成员函数有一个隐藏参数,名为this 指针
void setcolor(int color) { m_color = color; }
void setcolor(int color, (CShape*)this) { this->m_color = color; }
4.虚拟函数与多态(Polymorphism)
1.要调用父类别的函数,你必须使用scope resolution operator(::)明白指出。
接下来我要触及对象类型的转换,这关系到指针的运用,更直接关系到为什么需要虚拟
函数。了解它,对于application framework 如MFC 者的运用十分十分重要。
接下来我要触及对象类型的转换,这关系到指针的运用,更直接关系到为什么需要虚拟
函数。了解它,对于application framework 如MFC 者的运用十分十分重要。
2. 如果你以一个「基础类别之指针」指向「衍生类别之对象」,那么经由该指针
你只能够调用基础类别所定义的函数。
你只能够调用基础类别所定义的函数。
3. 如果你以一个「衍生类别之指针」指向一个「基础类别之对象」,你必须先做
明显的转型动作(explicit cast)。这种作法很危险,不符合真实生活经验,在
程序设计上也会带给程序员困惑。
明显的转型动作(explicit cast)。这种作法很危险,不符合真实生活经验,在
程序设计上也会带给程序员困惑。
4. 如果基础类别和衍生类别都定义了「相同名称之成员函数」,那么透过对象指
针调用成员函数时,到底调用到哪一个函数,必须视该指针的原始型别而定,
而不是视指针实际所指之对象的型别而定。
针调用成员函数时,到底调用到哪一个函数,必须视该指针的原始型别而定,
而不是视指针实际所指之对象的型别而定。
虚拟函数正是为了对「如果你以一个基础类别之指针指向一个衍生类别之对象,那么透
过该指针你就只能够调用基础类别所定义之成员函数」这条规则反其道而行的设计。
过该指针你就只能够调用基础类别所定义之成员函数」这条规则反其道而行的设计。
如果没有虚拟函数这种东西,你还是可以使用scope resolution operator(::)明白指出调用
哪一个函数,但程序就不再那么优雅与弹性了。
哪一个函数,但程序就不再那么优雅与弹性了。
虚拟函数与document 有关的Serialize 函数和与view 有关的OnDraw 函数
多态(Polymorphism)
编译器无法在编译时期判断pEmp->computePay到底是调用哪一个函数,必须在执行时期才能评估之,这称为后期绑定late binding 或动
态绑定dynamic binding。
态绑定dynamic binding。
纯虚拟函数
virtual void display() = 0;virtual void display() = 0;
只要是拥有纯虚拟函数的类别,就是一种抽象类别,它是不能够被具象化(instantiate)的
■ 如果你期望衍生类别重新定义一个成员函数,那么你应该在基础类别中把此函
数设为virtual。
■ 以单一指令唤起不同函数,这种性质称为Polymorphism,意思是"the ability to
assume many forms",也就是多态。
■ 虚拟函数是C++ 语言的Polymorphism 性质以及动态绑定的关键。
77
■ 既然抽象类别中的虚拟函数不打算被调用,我们就不应该定义它,应该把它设
为纯虚拟函数(在函数声明之后加上"=0" 即可)。
■ 我们可以说,拥有纯虚拟函数者为抽象类别(abstract Class),以别于所谓的
具象类别(concrete class)。
■ 抽象类别不能产生出对象实体,但是我们可以拥有指向抽象类别之指针,以便
于操作抽象类别的各个衍生类别。
■ 虚拟函数衍生下去仍为虚拟函数,而且可以省略virtual 关键词。
数设为virtual。
■ 以单一指令唤起不同函数,这种性质称为Polymorphism,意思是"the ability to
assume many forms",也就是多态。
■ 虚拟函数是C++ 语言的Polymorphism 性质以及动态绑定的关键。
77
■ 既然抽象类别中的虚拟函数不打算被调用,我们就不应该定义它,应该把它设
为纯虚拟函数(在函数声明之后加上"=0" 即可)。
■ 我们可以说,拥有纯虚拟函数者为抽象类别(abstract Class),以别于所谓的
具象类别(concrete class)。
■ 抽象类别不能产生出对象实体,但是我们可以拥有指向抽象类别之指针,以便
于操作抽象类别的各个衍生类别。
■ 虚拟函数衍生下去仍为虚拟函数,而且可以省略virtual 关键词。
5.类别与对象大解剖
含有虚函数的类必定有一个指向虚函数表的指针
#include<iostream>
using namespace std;
class ClassA
{
public:
int m_dataOne;
int m_dataTwo;
public:
void funcOne() { }
void funcTwo() { }
virtual void vfuncOne() { }
virtual void vfuncTwo() { }
};
class ClassB : public ClassA
{
public:
int m_dataThree;
public:
void funcTwo() { }
virtual void vfuncOne() { }
};
class ClassC : public ClassB
{
public:
int m_dataOne;
int m_dataFour;
public:
void funcTwo() { }
virtual void vfuncOne() { }
};
void main()
{
std::cout<<sizeof(ClassA)<<std::endl;
std::cout<<sizeof(ClassB)<<std::endl;
std::cout<<sizeof(ClassC)<<std::endl;
ClassA a;
ClassB b;
ClassC c;
b.m_dataOne = 1;
b.m_dataTwo = 2;
b.m_dataThree = 3;
c.m_dataOne = 1;
c.m_dataTwo = 2;
c.m_dataThree = 3;
c.m_dataFour = 4;
c.ClassA::m_dataOne = 111;
cout<<b.m_dataOne<<endl;
cout<<b.m_dataTwo<<endl;
cout<<b.m_dataThree<<endl;
cout<<c.m_dataOne<<endl;
cout<<c.m_dataTwo<<endl;
cout<<c.m_dataThree<<endl;
cout<<c.m_dataFour<<endl;
cout<<c.ClassA::m_dataOne<<endl;
cout<<&b<<endl;
cout<<&b.m_dataOne<<endl;
cout<<&b.m_dataTwo<<endl;
cout<<&b.m_dataThree<<endl;
cout<<&c<<endl;
cout<<&c.m_dataOne<<endl;
cout<<&c.m_dataTwo<<endl;
cout<<&c.m_dataThree<<endl;
cout<<&c.m_dataFour<<endl;
cout<<&c.ClassA::m_dataOne<<endl;
system("pause");
}
6.Object slicing 与虚拟函数
#include <iostream>
using namespace std;
class CObject
{
public:
virtual void Serialize()
{
cout<<"COject::Serialize() \n\n";
}
};
class CDocument : public CObject
{
public:
int m_dataOne;
virtual void Serialize()
{
cout<<"CDocument::Serialize() \n\n";
}
void func()
{
cout<<"CDocument::func()"<<endl;
Serialize();
}
};
class CMyDoc : public CDocument
{
public:
int m_dataTwo;
virtual void Serialize()
{
cout<<"CMyDoc::Serialize() \n\n";
}
};
void 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<<"# 4testing"<<endl;
((CDocument)mydoc).func();
system("pause");
}
经过data slicing 把mydoc变成了一个完完全全的CDocument对象了。
其中(CDocument)mydoc强制转换将会把对象的内容分割。
7.静态成员(函数和变量)
static double m_rate;
static成员变量不是对象的一部分,而是类的一部分,所以程序还没有诞生对象的时候就由此成员变量了。
static成员变量不要安排在构造函数中,也不要安排在头文件中,因为它可能被执行很多次。
double Saving::m_rate = 0.0075
Saving::m_rate = 0.0075 //需把m——rate改为public。
如果希望在产生object之前就存取class中的private static成员变量,就设计一个static成员函数。
static void setRate(double newRate){m_rate = newRate;}
8.C++程序的生与死:构造与析构函数
1.new 不但配置对象所需的内存空间,而且会引发构造函数的执行。
2. 全局对象:程序开始就构造函数,程序结束前析构。
静态对象:程序诞生时构造函数执行,程序结束前析构但是比全局对象早。
new出来的局部对象:诞生时构造函数执行,delete时析构。
局部对象:执行到的时候构造,局部函数结束时析构。