现在的位置: 首页 > 综合 > 正文

C++ primer:命名空间与多重继承

2019年02月07日 ⁄ 综合 ⁄ 共 3764字 ⁄ 字号 评论关闭

1.库倾向于定义许多全局名字——主要是模板名、类型名或函数名。在使用来这些名字中有一些几乎不可避免地会发自多个供应商的库编写应用程序的时候,生冲突,这种名字冲突问题称为命名空间污染问题。

2.命名空间为防止名字冲突提供了更加可控的机制,个命名空间是一个作用域,通过在命名空间内部定义库中的名字,库的作者(以及用户)可以避免全局名字固有的限制。

3.命名空间定义以关键字 namespace 开始,后接命名空间的名字。命名空间可以在全局作用域或其他作用域内部定义,但不能在函数或类内部定义。

4.命名空间作用域不能以分号结束。

namespace cplusplus_primer {
class Sales_item { /* ... */};
Sales_item operator+(const Sales_item&,
const Sales_item&);
class Query {
public:
Query(const std::string&);
std::ostream &display(std::ostream&) const;
// ...
};
class Query_base { /* ... */};
}

5.命名空间外部的代码必须指出名字定义在哪个命名空间中:

cplusplus_primer::Query q =
cplusplus_primer::Query("hello");
q.display(cout);

6.像对 std 中定义的命名空间所做的那样,可以编写 using 声明(第 3.1 节)来获得对我们知道将经常使用的名字的直接访问:

using cplusplus_primer::Query;

7.一个命名空间的分离部分可以分散在多个文件中,在不同文本文件中的命名空间定义也是累积的。

8.定义在全局作用域的名字(在任意类、函数或命名空间外部声明的名字)是定义在全局命名空间中的。全局命名空间是隐式声明的,存在于每个程序中。

9.嵌套命名空间中的名字遵循常规规则:外围命名空间中声明的名字被嵌套命名空间中同一名字的声明所屏蔽。

10.未命名的命名空间与其他命名空间不同,未命名的命名空间的定义局部于特定文件,从不跨越多个文本文件。未命名的命名空间以关键字 namespace 开头,接在关键字 namespace 后面的是由花括号定界的声明块。

11.一个 using 声明一次只引入一个命名空间成员,从 using 声明点开始,直到包含 using 声明的作用域的末尾,名字都是可见的。

12.可用命名空间别名将较短的同义词与命名空间名字相关联

namespace primer = cplusplus_primer;

13.using 指示以关键字 using 开头,后接关键字 namespace,再接命名空间名字。.使得特定命名空间所有名字可见,没有限制。

14.可以尝试用 using 指示编写程序,但在使用多个库的时候,这样做会重新引入名字冲突的所有问题。

15.对命名空间内部使用的名字的查找遵循常规 C++ 查找规则:当查找名字的时候,通过外围作用域外查找。对命名空间内部使用的名字而言,外围作用域可能是一个或多个嵌套的命名空间,最终以全包围的全局命名空间结束。

namespace A {
int i;
namespace B {
int i;
// hides A::i within B
int j;
int f1()
{
int j;
// j is local to f1 and hides A::B::j
return i; // returns B::i
}
} // namespace B is closed and names in it are no longer visible
int f2() {
return j;
// error: j is not defined
}
int j = i;
// initialized from A::i
}

16.可以从函数的限定名推断出查找名字时所检查作用域的次序,限定名以相反次序指出被查找的作用域。限定符 A::C1::f3 指出了查找类作用域和命名空间作用域的相反次序,首先查找函数 f3 的作用域,然后查找外围类 C1 的作用域。在查找包含 f3 定义的作用之前,最后查找命名空间 A 的作用域。

17.接受类类型形参(或类类型指针及引用形参)的函数(包括重载操作符),以及与类本身定义在同一命名空间中的函数(包括重载操作符),在用类类型对象(或类类型的引用及指针)作为实参的时候是可见的。

std::string s;
// ok: calls std::getline(std::istream&, const std::string&)
getline(std::cin, s);

当编译器看到 getline 函数的使用getline(std::cin, s的时候,它在当前作用域,包含调用的作用域以及定义 cin 的类型和string 类型的命名空间中查找匹配的函数。因此,它在命名空间 std 中查找并找到由 string 类型定义的 getline 函数。

18.在多重继承下,派生类的对象包含每个基类的基类子对象,构造派生类型的对象包括构造和初始化所有基类子对象

class Bear : public ZooAnimal {};
class Panda : public Bear, public Endangered {};

19.构造函数调用次序既不受构造函数初始化列表中出现的基类的影响,也不受基类在构造函数初始化列表中的出现次序的影响。

20.基类构造函数按照基类构造函数在类派生列表中的出现次序调用。总是按构造函数运行的逆序调用析构函数

21.在单个基类情况下,派生类的指针或引用可以自动转换为基类的指针或引用,对于多重继承也是如此,派生类的指针或引用可以转换为其任意基类的指针或引用。

void print(const Bear&);
void highlight(const Endangered&);
ostream& operator<<(ostream&, const ZooAnimal&);
Panda ying_yang("ying_yang");// create a Panda object
print(ying_yang);// passes Panda as reference to Bear
highlight(ying_yang); // passes Panda as reference to Endangered
cout << ying_yang << endl; // passes Panda as reference to ZooAnimal

22.编译器不会试图根据派生类转换来区别基类间的转换,转换到每个基类都一样好。

void print(const Bear&);
void print(const Endangered&);

Panda ying_yang("ying_yang");
print(ying_yang);// error: ambiguous

23.像单继承一样,用基类的指针或引用只能访问基类中定义(或继承)的成员,不能访问派生类中引入的成员。

24.假定所有根基类都将它们的析构函数适当定义为虚函数,那么,无论通过哪种指针类型删除对象,虚析构函数的处理都是一致的:

25.当一个类有多个基类的时候,通过所有直接基类同时进行名字查找。多重继承的派生类有可能从两个或多个基类继承同名成员,对该成员不加限定的使用是二义性的,即使两个继承的函数有不同的形参表,也会产生错误。类似地,即使函数在一个类中是私有的而在另一个类中是公用或受保护的,也是错误的。。名字查找总是以两个步骤发生(第 7.8.1 节):首先编译器找到一个匹配的声明(导致二义性),然后,编译器才确定所找到的声明是否合法。

26.虚继承是一种机制,类通过虚继承指出它希望共享其虚基类的状态。在虚继承下,对给定虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象。共享的基类子对象称为虚基类

27.通过用关键字 virtual 修改声明,将基类指定为通过虚继承派生。

// the order of the keywords public and virtual is not significant
class Raccoon : public virtual ZooAnimal { /* ... */ };
class Bear : virtual public ZooAnimal { /* ... */ };

28.指定虚派生只影响从指定了虚基类的类派生的类。除了影响派生类自己的对象之外,它也是关于派生类与自己的未来派生类的关系的一个陈述。

29.可以无二义性地直接访问共享虚基类中的成员。同样,如果只沿一个派生路径重定义来自虚基类的成员,则可以直接访问该重定义成员。在非虚派生情况下,两种访问都可能是二义性的

1. 如果在每个路径中 X 表示同一虚基类成员,则没有二义性,因为共享该成员的单个实例。
2. 如果在某个路径中 X 是虚基类的成员,而在另一路径中 X 是后代派生类的成员,也没有二义性——特定派生类实例的优先级高于共享虚基类实例。
3. 如果沿每个继承路径 X 表示后代派生类的不同成员,则该成员的直接访问是二义性的。

30.通常,每个类只初始化自己的直接基类。在应用于虚基类的进修,这个初始化策略会失败。如果使用常规规则,就可能会多次初始化虚基类。为了解决这个重复初始化问题,在虚派生中,由最低层派生类的构造函数初始化虚基类。

31.无论虚基类出现在继承层次中任何地方,总是在构造非虚基类之前构造虚基类

抱歉!评论已关闭.