序言
expliucuit 禁止编译器执行非预期的隐式类型转换
#include <iostream>
using namespace std;
class A
{
private:
int a;
public:
explicit A(int a)
{
this->a = a;
}
void show()
{
cout<<"a:"<<a<<endl;
}
};
void doSomething(A obj)
{
cout<<"ok"<<endl;
}
int main()
{
A a(10);
doSomething(a);
doSomething(10);
return 0;
}
不加explicit 执行ok
加入后:隐式转换被禁止
:
除非有好的理由允许构造函数被用与隐式类型转换,否则最好声明为explicit
二.
拷贝构造函数的实现方式:
Widget(const Widget &abc);
Widget& operator =(const Wideget &abc);
以Pass-by-value传递用户自定义类型通常是坏主意,Pass-by-reference-to-const往往是最好的选择。
三.统一一套属于自己的命名习惯:
四.多花精力关注线程的种种问题和TR1,Boost
Accustoming yourself to C++
条款一:
C++ 多重范型编程语言(multi paradigm programming language)
支持过程形式(procedural) 面向对象形式(object-oriented) 函数形式(functional) 泛型形式(generic)
元编程形式(meta programming)
C++是由四个次语言(sublanguage)集合而成的语言联邦(a federation of languages)
C语言+C with Classes + template + STL
条款二:
尽量以const,enum,inline替换#define
Prefer consts,enums,and inlines to #define
”宁可以编译器替换预处理器“比较好,因为#define不被视为语言的一部分。在编译器处理源码之前就一起被预处理器移走了
,记号名称没有进入记号表(symbol table)中。
解决之道:以一个常量替换上述的宏(#define) :
const double AspectRatio = 1.5;
两种特殊情况:第一个注意的情况是:定义常量指针(constant pointers)
const char * const authorname = ”Scott Meyers“
第二个注意的情况是:class专属常量,为了将常量的作用域(scope)限制于class内,你必须保证它成为class的一个成员
(member)而为确保此常量至多只有一份实体,你必须让他成为一个static成员。
class GamePlayer
{
private:
static const int NumTurns = 5; //常量声明式
int scores[NumTurns];
};
const int GamePlayer::NumTurns; //NumTurns的定义式
如果你的编译器不允许”static 整型数class常量完成“in class初值设定".可改用所谓的”the enum hack 补偿法"
其理论基础是:一个属于枚举型的数值可以权充int S被使用,于是GamePlayer定义如下:
class GamePlayer
{
private:
enum{NumTurns = 5}
int scores{NumTurns};
};
//enum hack行为某方面来说比较像#define而不像const,取一个const变量的地址是合法的
但是取一个enum的地址就不合法,而取一个#define的地址通常也不合法。
如果你不想别人获得一个pointer或reference指向你某个整数常量。enum可以帮你实现这个约束。且它不会
导致非必要的内存分配。
另外,作为实用enum hack行为是第三个次语言template metaprogramming的基础技术。
段错误返回信息:SIGSEGV是当一个进程执行了一个无效的内存引用
#include <iostream>
#define CALL_WITH_MAX(a,b) ((a)>(b)?(a):(b))
using namespace std;
int main()
{
int a = 5 , b = 0;
CALL_WITH_MAX(++a,b);
printf("%d\n",a);
CALL_WITH_MAX(++a,b+10);
printf("%d\n",a);
return 0;
}
#include <iostream>
#define CALL_WITH_MAX(a,b) ((a)>(b)?(a):(b))
using namespace std;
int main()
{
int a = 5 , b = 0;
CALL_WITH_MAX(++a,b+10);
printf("(++a,b+10):%d\n",a);
CALL_WITH_MAX(++a,b+10);
printf("(++a,b+10): %d\n",a);
return 0;
}
为了防止宏函数的缺点导致遭遇麻烦,可以获得宏带来的效率以及一般函数的所有可预料行为和类型安全性
采用template inline函数
template<typename T>
inline void callWithMax(const T&a , const T &b)
{
f(a>b?a:b);
}
//不像宏定义,模板函数遵守作用域(scope)和访问规则。
有了const,enum和inlines 对预处理器(#define)的需求降低了许多。
条款三:尽可能使用const
Use const wheneber possible
const允许你指定一个语义约束(指定一个“不该被改动的对象”)而编译器会强制实施这项约束。
关键字const可以在classes外部修饰global或namespace作用域中的常量或修饰文件,函数,或区块作用域(block scope)中被声明为static的对象。你可以用它修饰classes内部的static和non-static成员变量。面对指针,你可以指出指针自身,和指针所指物或两者都(或都不是)const
const char *p = greeting const在*号左边,修饰内容是不可更改,指针可更改
const * char p = greeting 或 char * const p = greeting (两种写法都OK的) const在*号右边,修饰指针不可更改,指针指向内容可更改。
const char * const p = greeting 两者都是不可被更改的类型
STL迭代器
如果你希望迭代器的内容不可被改动(即希望STL模拟一个const T*指针)你需要的是const_iterator
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin()
*iter = 10;
++iter; //错误,iter是const类型的
std::vector<int>::const_iterator cIter = vec.begin()
*cIter = 10; //错误,*cIter是const
++cIter;
const最具威力的用法是面对函数声明时的应用。在一个函数声明式子里const可以和函数返回值,各参数,函数自身(如果是成员函数的)产生关联。
const Rational operator *(const Ration &s,const Ration &c)
为什么返回值要加const?因为const可以避免下列情况
Ration a,b,c
(a*b)= c; //在a*b的成果上调用operator = 在这种情况下const可以避免无端地与内置类型不兼容。
const成员函数
*//
请记住
1.将某些东西声明为const可帮助编译器侦测出错误用法,const可被施加于任何作用域里的对象,函数参数,函数返回值类型,成员函数本地。
2.编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)
3.当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。