导读精华
explicit,可以阻止它们被用来执行隐式类型转换,但它们仍可被用来进行显示类型转换。
一般explicit构造函数比non-explicit更受欢迎,因为它们禁止编译器执行非预期(往往也不被期望)的类型转换。(除非你自己需要隐式类型转换,否则就设置成explicit)
class C{
public:
explicit C(int x);
};
copy构造函数,以同类型对象初始化自我对象。它定义了一个对象如何passed by value(在函数调用中会用到也就是复制参数时,就是走的copy构造函数)
class Widget
{
public:
Widget();//默认构造函数
Widget(const Widget& rhs);//copy构造函数
Widget& operator=(const Widget& rhs);//copy assignment操作符
};
Widget w1;//调用default构造函数
Widget w2(w1);//调用copy构造函数
w1=w2;//调用copy assignment操作符
Widget w3=w2;//调用copy构造函数
by-value传递用户自定义类型通常不好,一般pass-by-reference-to-const比较好针对自定义类型
1:视C++为一个语言联邦
最开始C++只是C加上一些面向对象特性。
逐渐发展加入了很多东西,现在已经是个多重范型编程语言了,支持
- 过程形式
- 面向对象形式
- 泛型形式
- 函数形式
- 元编程形式
可以把C++理解成由相关语言组成的联邦而不是单一语言。
主要次语言
- C:就是传统的C语言部分,包括区块,语句,预处理器,内置数据类型,数组,指针
- Object-Oriented C++:包含了classes,封装,继承,多态,virtual函数(动态绑定)
- Template C++:这是泛型编程的部分,也就是template。由于模板太强大,它带来了新的编程范型,即模板元编程
- STL:包括容器,迭代器,算法以及函数对象。
不同的次语言有不同的高校编程守则要求:
对C内置类型(比如int这种)而言pass-by-value一般比传递引用更高效,但是在Object Oriented C++中由于用户自定义构造函数和析构函数的存在,传递const引用往往更好,运用Template C++同样如此,因为你都不知道对象类型是什么。
然而在STL中,迭代器和函数对象都是在C指针上面塑造出来的,所以STL的迭代器和函数对象,C的传递值守则更好。
2:尽量以const,enum,inline替换#define
涉及到编译器的处理,暂时看不懂。先记下结论吧
宁可以编译器替换预处理器(#define可能不被视为语言的一部分。)
#define A 1
替换为
const double A=1;
3:尽可能使用const
const在星号左边,表示被指物是常量,如果出现在星号右边,说明指针本身是常量。
const int *a;//a指向的东西是个常量
int *const a;//a指针是个常量,指向的东西可以变换
const可以写在类型前,也可以在类型之后,效果一样的。
cosnt A* a;
A cosnt *a;
STL迭代器是以指针为根据弄出来的。理解为T*
所以声明迭代器为const,也就是声明了一个常量指针,T* const,表名这个迭代器不能改变,但是它指向的东西的值可以改变。
这里很可能理解错为:
const iterator;
展开就是
const T*;
所以就是迭代器指向的东西不能变,但是迭代器可以改变指向的东西。
这种理解就是错误的。
因为始终要把迭代器理解成一个T*整体,那么加上const就是修饰这个整体,也就是修饰这个指针,也就是指针是个常量,不能够简单展开去理解。
例子
std::vector<int> vec;
const std::vector<int>::iterator iter=vec.begin();
*iter=10; //可以的,可以改变指向的东西的值
++iter; //错误的,不能改变指向
那么如果要想迭代器指向的东西不能改变怎么办?可以使用const_iterator
std::vector<int>::const_iterator iter=vec.begin();
*iter=10; //错误,迭代器是const_iterator
++iter; //正确,可以改变指向
non-const operator[]的返回类型是一个reference
很好理解,下面这个左边必须得是引用才能真正修改值。
tb[0]='x';
其它
mutable关键字的成员变量在const成员函数内也可以更改。
不要在const函数内调用non-const函数。
4:确定对象被使用前已先被初始化
int x;
class Point
{
int x,y;
};
Point p;
并不会保证x为0。
值没初始化用起来就靠人品了。。。。。
array是C++的C部分,不保证其内容被初始化,而vector有保证。
除了内置类型以外的任何其他东西,初始化责任落在构造函数。(确保每个成员都初始化)
不要混淆赋值和初始化
这是赋值。
class PhoneNumber{...};
class ABEntry{
public:
ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones);
private:
std::string theName;
std::string theAddress;
std::list<PhoneNumber>& phones
}
ABEntry::ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones)
{
theName=name;
theAddress=address;
thePhones=phones;
}
这是初始化
ABEntry::ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones):theName(name),theAddress(address),thePhones(phones),numTimesConsulted(0)
{
}
一定要这样写,效率更高。
效率原因
没怎么看懂,书上说赋值版本是先调用默认构造函数然后再赋值(所以默认构造函数的一切就浪费了),初始化版本是直接调用copy构造函数。
但我感觉赋值版本也不是调用默认构造函数呢?应该是赋值=符号吧。
static对象
生命从构造出来到程序结束为止。
类型包括全局对象,namespace作用域内的对象。
class内,函数内(局部static对象),以及file作用域内被声明成static的对象。
当一个文件引用另一个文件的非局部静态对象时,可能出现另一个对象还没有初始化
解决方案
把非局部静态对象搬到自己的函数内,函数返回引用。
FileSystem& tfs()
{
static FileSystem fs;
return fs;
}
Directory& tempDir()
{
static Directory td;
return td;
}
C++会保证,局部静态对象会在该函数调用期间首次遇上该对象之定义式时被初始化。
但这样需要注意在多线程环境下出问题。(可以在程序单线程启动时,手动调用这些方法)
TODO:验证这样反复调用会不会出现问题