C++ Knowledge series Layering
Programming has its own methodology.
Layering is everywhere in real life,this why the pruchase and sale exist.
Layering, Insert additional indirection
- 通过分层layering/add indirection来体现 "有一个" 或 "用...来实现’ composition /composite
- 使某个类的对象成为另一个类的数据成员,从而实现将一个类构筑在另一个类之上,这一过程称为 "分层"(Layering).
- User GUI Application Layer Domain Layer (Business Logical Tier) Infrastructure Layer (OS, DB tier).
- A little difference from the private inheritance which also means a kind of has-a or use-implementation relationship.
- 当通过分层使两个类产生联系时,实际上在两个类之间建立了编译时的依赖关系dependent。Separate implementation from interface, decouple, decompose. Composition
- 区分继承inheritance和模板template. 涉及到"类的行为"behavior of class 和 "类所操作的对象的类型"之间的关系。
- Behavior of class and type of being operated
- 当对象的类型不影响affect/reflect类中函数的行为behaviour/behavior时,就要使用模板来生成这样一组类。
- 当对象的类型影响类中函数的行为时,就要使用继承来得到这样一组类。
- 私有继承的含义不是 "是一个"。Has-a or use-implementation
- 关于私有继承的第一个规则rule正如你现在所看到的:和公有继承相反,如果两个类之间的继承关系为私有,编译器一般不会将派生类对象(如Student)转换成基类对象(如Person)。第二个规则是,从私有基类继承而来的成员都成为了派生类的私有成员,即使它们在基类中是保护或公有成员。行为特征character就这些。
- 私有继承意味着 "用...来实现"。如果使类D私有继承于类B,这样做是因为你想利用类B中已经存在的某些代码,而不是因为类型B的对象和类型D的对象之间有什么概念上的关系。因而,私有继承纯粹是一种实现技术technique。私有继承意味着只是继承实现,接口会被忽略。如果D私有继承于B,就是说D对象在实现中用到了B对象,仅此而已。私有继承在软件 "设计" design过程中毫无意义meaningless/insignificant,只是在软件 "实现" 时才有用。
- private inheritance, private or protected new, delete, constructor, destructor, friend class, all of these can be as trick when coding.
- 模板template是C++最有用useful的组成部分之一,但一旦开始经常性地使用它,你会发现,如果实例化一个模板一百次,你就可能实例化了那个模板的代码一百次。模板导致的 "代码膨胀"extension of code。
- 有时私有继承是表达类之间 "用...来实现" 关系的唯一有效effective途径。所以,当私有继承是你可以使用的最合适的实现方法时,就要大胆地boldly使用它。同时,广泛意义上来说,far and wide/widely/ by and large分层是应该优先采用的技术,所以只要有可能,就要尽量使用它。
- 如果一个派生类从多个基类继承了一个成员名,所有对这个名字的访问都是二义ambiguity的;你必须明确地说出你所指的是哪个成员。
- 这是一个集纯虚函数,简单虚函数和内联函数,综合应用之大成的方法,值得牢记在心。引入introduce/import新类 加入一个间接层。Indirect layer
- 如果真的将A声明为B和C的虚基类 virtual base class which is apparent only once within hierarchy structure,往往会在间和时间上强加给用户额外的开销extra cost。因为虚基类常常是通过对象指针来实现的 implemented by the pointer to object of class,并非对象本身。自不必说,内存中对象的分布layout/distribution是和编译器compiler相关的,但一条不变的事实是:如果A作为 "非虚" 基类,类型D的对象在内存中的分布通常占用连续的continual/consecutive内存单元;如果A作为 "虚" 基类,有时,类型D的对象在内存中的分布占用连续的内存单元,但其中两个单元unit包含的是指针,指向包含虚基类数据成员的内存单元。
- 一个空类void /empty class什么时候不是空类? ---- 当C++编译器通过它的时候。如果你没有声明下列函数,体贴的considerate编译器会声明它自己的版本where needed。这些函数是:一个拷贝构造函数copy constructor,一个赋值运算符assignment operator,一个析构函数destructor,一对取址运算符address-of operator。另外,如果你没有声明任何构造函数constructor,它也将为你声明一个缺省构造函数。所有这些函数都是公有的。 If a class is empty, there is in fact a char[1] in the class.
- const CString& operator=(const CString& stringSrc);
- Empty* operator&(); //for ordinary object
- const Empty* operator&() const;//for const object
- 生成的析构函数一般是非虚拟的,除非它所在的类是从一个声明了虚析构函数的基类继承而来。
- 缺省拷贝构造函数(赋值运算符)对类的非静态数据成员进行 "以成员为单位的memberwise" 逐一拷贝构造(赋值)
- 声明了至少一个构造函数constructor with some parameter but not itself as parameter,编译器将不会生成缺省构造函数。
- 面对这样的难题,C++拒绝编译这段代码。如果想让一个包含引用成员reference member or pointer 的类支持赋值,你就得自己定义赋值运算符。对于包含const成员的类(例如上面被修改的类中的objectValue)来说,编译器的处理也相似;因为修改const成员是不合法的,所以编译器在隐式生成赋值函数时也会不知道怎么办。还有,如果派生类的基类将标准赋值运算符声明为private, 编译器也将拒绝decline为这个派生类生成赋值运算符。因为,编译器为派生类生成的赋值运算符也应该处理基类部分,但这样做的话,就得调用对派生类来说无权访问的基类成员函数,这当然是不可能的。Make impossibility possible.
- 这是因为,确定非局部静态对象初始化的 " 正确" 顺序order很困难,非常困难,极其困难。即使在它最普通的形式下 ---- 多个被编译单元,多个通过隐式模板实例化所生成的非局部静态对象(隐式implicitly模板实例化时,它们本身可能都会产生这样的问题) ---- 不仅不可能确定正确的初始化顺序,往往连找一个可以确定正确顺序的特殊情况都不值得。
- 虽然关于 "非局部"non-local static object 静态对象什么时候被初始化,C++几乎没有做过说明;但对于函数中的静态对象(即,"局部" local static object静态对象)什么时候被初始化,C++却明确指出:它们在函数调用过程中初次碰到对象的定义时被初始化。所以,如果你不对非局部静态对象直接访问,而用返回局部静态对象引用的函数调用来代替,就能保证从函数得到的引用指向的是被初始化了的对象。这样做的另一个好处是,如果这个模拟非局部静态对象的函数从没有被调用,也就永远不会带来对象构造和销毁destroy的开销cost;而对于非局部静态对象来说就没有这样的好事。
- 这种返回引用reference return的函数虽然采用了上面所讨论的技术,但函数本身总是很简单:第一行定义并初始化initialize一个局部静态对象,第二行返回它,仅此而已。你可能很想把它声明为inline。条款33指出,对于C++语言规范的最新修订版本来说,这是一个非常有效的实现策略strategy/policy;但它同时指出,在使用之前,一定要确认你的编译器和标准中的相关要求要一致consistent。如果编译器不符合最新标准standard,你又象上面那样使用内联inline,就可能造成函数以及函数内部静态对象有多份拷贝 copy。这足以让一个成年的程序员哭泣. More copy of static object will be caused/brought about if the function within static object is defined as inline function.
- C++中有很多 "东西":C,重载overload(function with the same name but with the different parameter),覆盖override,面向对象Object-oriented,模板template,例外exception,名字空间namespace, abstract encapsulation inheritance polymorphism /composition
- 和传统开发工具及环境的兼容性compatibility。各色不同的开发环境到处都是,编译器compiler、链接器linker和编辑器editer则无处不在
- 解决真实actual/real/factual/the de facto问题的可应用性applicability。C++没有被设计为一种完美的perfect,纯粹的pure语言,不适于用它来教学生如何编程。它是设计为专业程序员的强大工具,用它来解决各种领域domain中的真实问题。
- 为什么隐式implicitly生成的拷贝构造函数和赋值运算符要象现在这样工作呢,尤其是指针(参见条款11和45)?因为这是C对struct进行拷贝和赋值的方式,和C兼容compatible很重要。
- 为什么析构函数不自动automatically被声明为virtual,为什么实现细节必须出现在类的定义definition中因为不这样做就会带来性能上capability/performance的损失,效率efficiency /performance很重要。
- 为什么C++不能检测非局部静态对象之间的初始化依赖关系dependent relationship呢?因为C++支持单独编译(即,分开编译源模块,然后将多个目标文件链接起来,形成可执行程序),依赖现有的链接器,不和程序数据库打交道。所以,C++编译器几乎不可能知道整个程序的一切情况。
- The Design and Evolution of C++. D&E == Design and Evolution
- 一个虚函数调用所使用的缺省参数default argument是表示对象的指针或引用的静态类型static type所决定的虚函数所声明的缺省参数。派生类中的重载函数不获取它重载的函数中的缺省值default value。
- International Standard for Information Systems----Programming Language C++. International Organization for Standardization (ISO)。
- 如果在 "D&E千里之外的视野" 和 "C++标准的微观世界" 之间存在承上启下的桥梁那就太好了。Beyond
- The Annotated C++ Reference Manual
- 所以,为了防患于未然,Stroustrup决定让派生类成员按名字隐藏掉hide所有得重载的基类成员 override all overloaded functions in base class,也无论是普通函数还是虚拟函数。No matter whether the member function is normal or virtual. Names in base are hidden by names in derived. 如果想使用基类被override的函数,可以采取using Base::function(),或者另写一个虚拟函数调用基类成员函数。