声明式(declaration)是告诉编译器某个东西的名称或类型,但略去细节:
extern int x; // 对象声明式
std::size_t numDigits(int number); //函数声明式
class Widget; // 类声明式
template<typename T> // 模板(template)声明式
定义式(definiton)的任务是提供编译器一些声明式所遗漏的细节。
对对象而言,定义式是编译器为此对象拨发内存的起点
对function 或function template ,定义式提供了代码本体。
对class 或 class template ,定义式列出它们的成员。
初始化(initialization)是“给予对象初值”的过程。对用户自定义对象---构造函数---默认构造函数(没有参数,或者每个参数都有缺省值)
被声明为explicit 的构造函数通常比其non-explici 兄弟更受欢迎,因为它们禁止编译器执行非预期(往往也不被期望)的类型转换。
这里就有一个建议,在没有必要的时候,将其写的严格一些,声明为explicit ,禁止隐式类型转换。此时如下代码将不再合法:
class B{
public:
explicit B(int x);
};
show_B(B& BOb);
show_B(B(1));// 合法
show_B(1);// 不合法
拷贝构造函数,拷贝赋值函数:
class Widget{
public:
Widget(); // 默认构造函数
Widget(const Widget & rhs); // 拷贝构造函数
Widget& operator = (const Widget& rhs); // 拷贝赋值操作符
//...
};
Widget w1; // 调用默认的构造函数
Widget w2(w1); // 调用拷贝构造函数
w1 = w2; // 调用拷贝赋值操作符
Widget w3 = w2; // 调用的是:拷贝构造函数。因为,这里有构造操作,单纯的赋值操作才会引发拷贝赋值操作符
拷贝构造函数是一个尤为重要的函数,因为它定义一个对象如何值传递。
bool hasAcceptableQuality(Widget w);//
//...
Widget aWidget;
if (hasAcceptableQuality(aWidget)) {
//参数 aWidget 以值传递给hasAcceptableQuality ,上述调用中aWidget 被复制到w 内,触发拷贝构造函数
}
值传递不是一个好办法(内存开销,隐式的拷贝构造函数产生的时间开销,容易出bug 等等),const 引用才是稳妥,高效,错误少的办法。
STL 中的很多功能以函数对象(function object)实现,那是“行为像函数”的对象,这样的对象来自于,重载了operator(),即(function call 函数调用)的类。下面我写个小例子:
class Mul
{
public:
Mul() {}
int operator()(int b) {
return b * 2;
}
};
Mul a;
cout << a(2) << endl;
不明确(未定义)行为的结果是不可预期的,如下:应该尽量的避免:
int *p = 0; // p 是一个null 指针
std::cout << *p; // 对一个null 指针取值,行为不明确
char name[] = "Darla"; // 大小为6,最后的null
char c = name[10]; // 指针指向一个无效的数组索引
// 行为不明确
cout << sizeof(name) << endl;
TR1:一份规范,描述加入C++ 标准程序库的诸多新机能。。。。。我举不出例子来暂时
Boost:一个组织,提供可移植、同僚复审、源码开放的一个C++ 库,大多数TR1 机能以Boost 的工作为基础。
条款1:View C++ as a federation of language
C++ 是一个多重范型语言(multiparadigm programing language),同时支持过程形式(procedural),面向对象(object-oriented)、函数形式(functional)、泛型形式(generic)、元编程形式(metaprogramming)的语言。这些能力和弹性使C++ 成为一个无可匹敌的工具,但也可能引发某些迷惑:所有“适当”用法似乎都有例外,如何做?
将C++ 视为一个由相关语言组成的联邦而非单一语言。但其某个次语言中,各种守则与通例都倾向简单、直观易懂、容易记住。然而,当从一个次语言转移到另一个次语言,守则可能会变:四个次语言:
1. C。C++ 以C 为基础,区块(blocks)、语句(statements)、预处理器(processor)、内置数据类型(build-in data types)、数组(arrays)、指针(pointers)等统统来自C。
2.面向对象C++。通常的C with Classes 诉求的:类(包括构造和析构),封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual 函数(动态绑定)等等。就是通常的面向对象设计
3. Template C++。C++ 的范型编程(generic programming)部分,它功能强大,带来的崭新的编程范型(programming paradigm),就是所谓的template meta programming(TMP,模板元编程)。
4. STL。STL 是一个template 程序库。它对容器、迭代器、算法以及函数对象(containers、iterators 、algorithm)以及函数对象(function objects)的规约有极佳的紧密配合与协调,然而template 及程序库也可以其他想法建设。使用STL 必须遵循其规则。
C++ 并不是一个带有一组守则的一体语言;它从四个次语言组成的联邦政府,每个次语言都有自己的规约。记住这四个次语言会发现C++ 容易了解的多。
# 尽量以const,enum,inline 替换#define
## #define 的缺点
### #define 是在预编译的时候,就被替换了,不会进入到符号表,此时,如果编译出错,很难定位
- 举例
- #define ASPECT_RATIO 1.653
- 编译器报告1.653 相关的错误时,比较难定位
- 另外,#define是简单的替换,也会多占内存
- 替换方法
- const double AspectRatio = 1.653;
### #define 没有作用域的概念,只要定义了,就会存在,无法定义class 专属常量
- 举例
- 略
- 替换方法
- const 和 enum 都有作用域的概念
### 实现宏,有时有歧义
- 举例
- #define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))
- CALL_WITH_MAX(++a,b);// 当a + 1 > b ,a被累加两次
- 替换方法
- template<typename T>
inline void callWithMax(const T&a,const T&b)
{f(a > b ? a : b)}
## const 替换#define 的主要分类
### 常量指针
- 注意事项
- 将指针声明为const
- 如果是字符串指针,使用const char* const
- 当然,当是字符串,我们可以使用string 类
### class 类专属常量
- 注意点
- 初始化
- 当使用const ,需要使用static,防止浪费内存
- 编译器支持类内初始化
- 直接类内声明时初始化
- 类外定义不需初值
- 编译器不支持类内初始化
- 类内声明
- 类外定义时赋初值
- 当使用enum,仅仅可代表整型
- 不需要考虑初始化,编译时就存在,不可取地址、引用
## 总结
### 对于单纯的常量,最好以const 对象或enum 替换#define
### 对于形似函数的宏,注意,这里说的是形似函数,有很多宏功能是无法用inline 替换的,最好改用inline 函数