读书笔记之:C++ Primer 第4版(ch15-18)

第15章 面向对象编程

面向对象中的三个基本概念:数据抽象,继承和动态绑定。

C++中利用类进行数据抽象,用类派生从一个类继承另一个类。动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数。

1. 多态性

面向对象的关键思想是多态性。在C++中,多态性仅用于通过继承而相关联的类型的引用或指针。

2. 继承

在C++中,基类必须指出希望派生类重定义哪些函数,定义为virtual的函数是基类期待派生类重新定义的。而那些基类希望派生类继承的函数不能定义为虚函数。

除了构造函数之外,任意非static成员函数都可以是虚函数。并且,保留字virtural只在类内部的成员函数声明中出现,不能用在类定义体外部出现的函数定义上。

public:任意外部的类和函数都可以访问

private:只有类本身和其友元可以访问。其子类也不行。

protected:不能被类的用户访问,但可以被派生类访问。

需要注意的是,派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊的访问权限

3. 派生类和虚函数

派生类中的虚函数的声明必须与基类中的定义方式完全匹配,但有一个例外:返回对基类型的引用(或指针)的虚函数。派生类中的虚函数可以返回基类函数所返回类型的派生类的引用(或指针)

友元关系与继承

友元关系不能继承,基类的友元对派生类的成员没有特殊访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系 的类。

构造函数与复制控制

1. 继承情况下的类作用域

在继承情况下,派生类的作用域嵌套在基类作用域中。如果不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义。

2.名字查找在编译时发生
对象、引用或指针的静态类型决定了对象能够完成的行为。甚至当静态类型和动态类型可能不同的时候,就像使用基类类型的引用或指针时可能会发生的,静态类型仍然决定着可以使用什么成员。
动态类型(dynamic type)是运行时类型。基类类型的指针和引用可以绑定到派生类型的对象,在这种情况下,静态类型是基类引用(或指针),动态类型是派生类引用或指针
静态类型(static type)是编译时类型。对象的静态类型和动态类型是相同的。引用或指针所引用的对象的动态类型可以不同于引用或指针的静态类型。


3. 名字冲突与继承与基类成员同名的派生类成员将屏蔽对基类成员的直接访问。但是,我们可以通过使用作用域操作符类访问被屏蔽的成员。
4. 虚函数与作用域要获得动态绑定,必须通过基类的引用或指针成员调用虚成员。当我们这样做时,编译器将在基类中查找函数。假定找到了名字,编译器就检查实参是否与形参匹配。现在可以理解为什么虚函数必须在基类和派生类中拥有同一原型了。如果基类成员与派生类成员接受的实参不同,就没有办法通过基类类型的引用或指针调用派生类函数了。注意,这儿有个例外是返回类型可以稍有不同。还有,const也是函数声明的一部分,所以同名(参数列表相同)的const函数和非const函数是不同的。 

如果子类定义了一个与基类的虚函数名称相同但是参数列表不同的函数时,该函数会屏蔽掉基类中的虚函数,虽然子类从基类继承了这个虚函数,但是却无法通过子类对象来调用该虚函数,因为该虚函数已经被屏蔽掉到了。如:

Base bobj; D1 d1obj; D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn(); // ok: virtual call, will call Base::fcn at run time
bp2->fcn(); // ok: virtual call, will call Base::fcn at run time
bp3->fcn(); // ok: virtual call, will call D2::fcn at run time

纯虚函数含有(或继承)一个或多个纯虚函数的类是抽象类。除了作为抽象基类的派生类对象的组成部分,不能够创建抽象类型的对象。可以声明抽象类的指针或引用。

5. 容器与继承对象不是多太的。
multiset<Item_base> basket;
Item_base base;
Bulk_item bulk;
basket.insert(base); // ok: add copy of base to basket
basket.insert(bulk); // ok: but bulk sliced down to its base part
上面的代码中可以看到,在加入派生类的对象时,只是将对象的基类部分保存在了容器之中。这儿相当于从派生类对象往基类对象复制,在复制的过程中,派生类对象将被切掉。不能将基类对象转换为派生类型,不存在这样的标准转换,可以将基类对象强制转换为派生类对象,但是这样做时会导致派生类部分的成员是未初始化的。可以使用容器保存对象的指针,但是这种情况下必须处理好对象和指针。(指针的情况下会出现动态绑定的问题

C++中面向对象编程中一个颇具讽刺意味的地方是,不能使用对象支持面向对象编程,相反,必须使用指针或引用。

第16章 模板与泛型编程
【1】


泛型编程是以独立于任何特定类型的方式编写代码。
模板定义以关键字 template 开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,形参之间以逗号分隔。
模板形参表不能为空。
1. 模板形参表
模板形参可以是表示类型的类型形参,也可以是表示常量表达式的非类型形参。
类型形参跟在关键字 class 或 typename 之后定义,例如,class T 是名为 T 的类型形参,在这里 class 和 typename 没有区别。
2. 使用函数模板
使用函数模板时, 编译器会推断哪个(或哪些)模板实参绑定到模板形参。一旦编译器确定了实际的模板实参,就称它实例化了函数模板的一个实例。实质上,编译器将确定用什么类 型代替每个类型形参,以及用什么值代替每个非类型形参。推导出实际模板实参后,编译器使用实参代替相应的模板形参产生编译该版本的函数。编译器承担了为我 们使用的每种类型而编写函数的单调工作。
【2】

typename与class之间是有差别的
在标准C++中,声明为typename的类型形参与声明为class的类型形参是没有区别的。但是,标准C++之前的系统有可能只支持使用关键字class来声明模板类型形参。
如果要在函数模板内部使用在类中定义的类型成员,必须在该成员名前加上关键字typename,以告知编译器将该成员当作类型。(因为默认情况下是当作类型成员的)
【3】


非类型模板形参
【4】


编写泛型程序
【5】


模板实例化
编译器产生模板的特定类型实例的过程称为实例化
模板在使用时将进行实例化,类模板在引用实际模板类类型时实例化,函数模板在调用它或用它对函数指针进行初始化或赋值时实例化。
类模板的每次实例化都会产生一个独立的类类型。
在使用函数模板时,编译器通常会为我们推断模板实参。
模板实参推断
模板类型形参可以用作一个以上函数形参的类型。在这种情况下,模板类型推断必须为每个对应的函数实参产生相同的模板实参类型。如果推断的类型不匹配,则调用将会出错:
【6】


类型转换的限制只适用与类型为模板形参的那些实参,对于普通类型定义的形参可以使用常规转换。
【7】

函数模板的显式实参
【8】


模板编译模型
【9】

非类型形参的模板实参
非类型模板实参必须是编译时常量表达式:
template <int hi, int wid>
class Screen {
public:
// template nontype parameters used to initialize data members
Screen(): screen(hi * wid, '#'), cursor (0),
height(hi), width(wid) { }
// ...
private:
std::string screen;
std::string::size_type cursor;
std::string::size_type height, width;
};
Screen<24,80> hp2621; // screen 24 lines by 80 characters
[10]

[11]


[12]


类模板的static成员
每个类的实例化都有自己的static成员,因为每个实例化都表示截然不同的类型。
像使用任意其他 static 数据成员一样,必须在类外部出现数据成员的定义。在类模板含有 static 成员的情况下,成员定义必须指出它是类模板的成员:

template <class T>
size_t Foo<T>::ctr = 0; // define and initialize ctr

模板特化

函数模板特化
【13】


与任意函数一样,函数模板特化可以声明而无须定义。模板特化声明看起来与定义很像,但省略了函数体:
// declaration of function template explicit specialization
template<>
int compare<const char*>(const char* const&,
const char* const&);
如果可以从函数形参表推断模板实参,则不必显式指定模板实参:

// error: invalid specialization declarations
// missing template<>
int compare<const char*>(const char* const&,
const char* const&);
【14】


// error: function parameter list missing
template<> int compare<const char*>;

// ok: explicit template argument const char* deduced from parameter types
template<> int compare(const char* const&,
const char* const&);

类模板的特化
类模板特化应该与它所特化的模板定义相同的接口,否则当用户试图使用未定义的成员时会感到奇怪。
在类特化外部定义成员时,成员之前不能加 template<> 标记。
类模板的部分特化
如果类模板有一个以上的模板形参,我们也许想要特化某些模板形参而非全部。使用类模板的部分特化可以做到这一点:

template <class T1, class T2>
class some_template {
// ...
};
// partial specialization: fixes T2 as int and allows T1 to vary
template <class T1>
class some_template<T1, int> {
// ...
};
类模板的部分特化本身也是模板。部分特化的定义看来像模板定义,这种定义以关键字 template 开头,接着是由尖括号(<>)括住的模板形参表。部分特化的模板形参表是对应的类模板定义形参表的子集

第17章 用于大型程序的工具
1. 命名空间
命名空间污染
像其他名字一样,命名空间的名字在定义该命名空间的作用域中必须是唯一的。命名空间可以在全局作用域或其他作用域内部定义,但不能在函数或类内部定义。
命名空间作用域不能以分号结束。



多重继承


虚继承



第18章 特殊工具与技术
优化内存分配


类成员的指针
成员指针包含类的类型以及成员的类型。
成员指针只应用于类的非 static 成员。static 类成员不是任何对象的组成部分,所以不需要特殊语法来指向 static 成员,static 成员指针是普通指针。
(1)数据成员指针

C++中的联合
为了与C进行兼容,C++中也有联合union。在C++中union是一种特殊的类。
一个 union 对象可以有多个数据成员,但在任何时刻,只有一个成员可以有值。当将一个值赋给 union 对象的一个成员的时候,其他所有都变为未定义的。
Union是没有静态数据成员、引用成员或类数据成员
某些(但不是全部)类特征同样
用于 union。例如,像任何类一样,union 可以指定保护标记使成员成为公用的、私有的或受保护的。默认情况下,union 表现得像 struct:除非另外指定,否则 union 的成员都为 public 成员。
union 也可以定义成员函数,包括构造函数和析构函数。但是,union 不能作为基类使用,所以成员函数不能为虚数。
union 不能具有静态数据成员或引用成员,而且,union 不能具有定义了构造函数、析构函数或赋值操作符的类类型的成员:

union illegal_members {
Screen s; // error: has constructor
static int is; // error: static member
int &rfi; // error: reference member
Screen *ps; // ok: ordinary built-in pointer type
};
这个限制包括了具有带
造函数、析构函数或赋值操作符的成员的类。

嵌套类
局部类

C++中从C语言继承来的不可移植特征:位域和volatile限定符
位域必须是整型数据类型,可以是 signed 或 unsigned。通过在成员名后面接一个冒号以及指定位数的常量表达式,指出成员是一个位域。

volatile 的确切含义与机器相关,只能通过阅读编译器文档来理解。使用 volatile 的程序在移到新的机器或编译器时通常必须改变。
用与定义 const 成员函数相同的方式,类也可以将成员函数定义为 volatile,volatile 对象只能调用 volatile 成员函数。

合成的复制控制不适用于 volatile 对象
对待 const 和 volatile 的一个重要区别是,不能使用合成的复制和赋值操作符从 volatile 对象进行初始化或赋值。合成的复制控制成员接受 const 形参,这些形参是对类类型的 const 引用,但是,不能将 volatile 对象传递给普通引用或 const 引用

 C++中的链接指示

posted @ 2012-03-05 00:07  Mr.Rico  阅读(904)  评论(0编辑  收藏  举报