Effective C++之一:让自己习惯C++
来源:互联网 发布:以撒的结合 mac 下载 编辑:程序博客网 时间:2024/06/03 20:25
0导读
(1)
extern int x; //加了extern是声明
int x;//定义
(2)隐式类型转换:
- 从小->大的转换中。比如从char转换为int。
- 从int->long。
- 自定义对象 派生类对象可以隐式转换为 基类对象。
而explicit阻止隐式类型转换。 经常使用在类的构造函数前面。
struct Month {
explicit Month(int m):val(m) {}
int val;
};
Date(2); //如果有explicit就会出错。如果没有,就可以隐式类型转换。
Date(Month(2));//使用构造函数将int显式转换
(3)
copy构造函数被用来“以同类型对象初始化自我对象”
copy赋值操作符被用来“从另一个对象中拷贝其值到自我对象”
主要区别在于:被赋值的对象是不是当前创建的,如果是,那就是调用的构造函数
Widget w1; //调用默认构造函数
Widget w2(w1); //调用copy构造函数
w1 = w2; //调用copy赋值操作符
Widget w3 = w2;//调用copy构造函数,因为是新对象被定义,一定会调用一个构造函数,不可能调用赋值操作。
(4)
int *p = 0;//p是一个null指针。一般来说内存赋值为 0。就是空。
(5)
命名习惯在 指针前加p,引用前加r
条款1 让自己习惯C++
(1)c++ 的 次语言
1、C
2、Object-Oriented C++ : classes、封装、继承、多态、virtual函数。
3、Template C++ :泛型编程部分。
4、STL
(2)C++高效编程视情况而变化,取决于使用c++哪部分。
条款2 尽量以const、enum、inline替换#define
(1)
宁可以编译器替换预处理器
(2)
#define RATIO1.653 //define语句在预处理阶段就替换了,编译器是看不见的,出现错误很难调试。这个名称并未进入记号表。
//大写名称用于宏
const double Ratio1.653;//作为一个常量,编译器肯定看的到,也会进入记号表内
(3)
无法利用#define创建一个class专属常量,因为#define并不重视作用域。
class GamePlayer{
private:
staticconst int NumTurns = 5;//常量声明式。如果是int、char、bool,只要不取它们的地址,就可以只声明并使用它们无需提供定义式
int scores[NumTurns];//使用该常量
...
};
const intGamePlayer::NumTurns;//定义式,某些编译器坚持要看到一个定义式。
//由于声明式已经赋值了,这就不能再赋值
旧编译器不允许声明式赋值,那就只能在定义式赋值。
class GamePlayer{
private:
staticconst int NumTurns;//位于头文件内
...
};
const intGamePlayer::NumTurns = 5;//位于实现文件内
如果在编译期间需要一个class常量的值,且编译器不允许声明式赋值,只能用“the enum hack”补偿做法。
理论基础是“一个枚举类型的数值可权充int被使用”
class GamePlayer{
private: //都是编译期间就搞定的
enum{ NumTurns =5};//the enum hack,令 NumTurns成为5的一个记号名称
int scores[NumTurns];//取const地址是合法的,但是取#define和enum地址是非法的
...
};
(4)
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b)) //必须加小括号,否则可能会有歧义
int a = 5, b =0;
CALL_WITH_MAX(++a, b);//a被累加两次
CALL_WITH_MAX(++a, b +10);//a被累加一次,调用f之前,a的递增次数取决于“它被拿来和谁比较”!
所以应该写为template inline函数
template<typename T>
inline void callWithMax(const T& a,const T& b)
{
f(a > b ? a : b); //由于不知道T是什么,可以用传递引用的方式
}
(5)总结
对于单纯常量,最好用const或enums代替#define
对于形似函数的宏,最好改用inline函数替换#define
条款3 尽可能使用const
(1)
const在*左边,物是常量
const在*右边,指针是常量
在左右,都是常量
void f1(const Widget* pw); //这两个意义相同,都是所指向的物是常量
void f2(Widget const* pw);
(2)迭代器中的情况:
const vector<int>::iterator it; // T* const 指针
//指针是常量,不能指向其他位置
vector<int>::const_iterator it; // const T*
//所指物是常量,所指的位置的值不能变
(3)
const面对函数声明时的应用:
1、const返回值 :避免返回值的赋值
2、const参数 :避免函数内部对这个参数的更改
3、const成员函数:避免更改任何的成员变量。注意:const对象只能调用const成员函数,而非const对象也可以调用const成员函数。
下面是const对象调用const成员函数的例子,返回的也是const
class TextBlock{
public:
... //两个成员函数如果只是常量性不一样是可以重载的。
constchar& operator[](size_t position)const //for const对象
{return text[position];}
char&operator[](size_t position)//for 非-const对象
{return text[position];}
private:
string text;
};
TextBlock tb("Hello");
cout<<tb[0];//调用非-const函数
tb[0] ='x';//ok。注意:返回的是引用,如果不是引用,那就是返回一个副本,不是想要的行为。
const TextBlock ctb("World");
cout<<ctb[0];//调用 const函数
ctb[0] ='x';//错误!企图对一个const返回值的对象进行更改也是错误的!
如果是char* 而不是 string。这个例子是说const对象的成员变量如果是指针,指针所指的内容也可能被改变,编译器是不会报错的。
class CTextBlock{
public:
CTextBlock(string s)
{pText = &s[0];}
char&operator[](size_t position)const //for const对象! 这有一个很重要的点:如果返回 const char& ,那main函数里面的指针赋值也是错误的。
{returnpText[position];}
void pprint()const
{
char* p =pText;
while ((*p) !='\0')
{
cout<<*p;
p++;
}
cout<<endl;
}
private:
char* pText;
};
int main()
{
constCTextBlock cctb("Hello");
char* pc = &cctb[0];//调用const成员函数取得一个指针
*pc = 'J'; //cctb现在是“Jello”
cctb.pprint();//cctb是const对象,所以只能调用const成员函数!此处输出 Jello
return0;
}
在C++中,mutable是为了突破const的限制而设置的。mutable 成员变量 在 const 成员函数也还可以被改变的
class CTextBlock{
public:
...
size_t length() const;
private:
char* pText;
mutable size_t textLength;//mutable 成员变量在 const成员函数也还可以被改变的
mutablebool lengthIsVaild;
};
size_t CTextBlock::length()const
{
if (!lengthIsVaild)
{
textLength = strlen(pText);
lengthIsVaild =true;
}
return textLength;
}
下面的例子是为了避免代码重复,设法让非const成员函数调用const成员函数。而不是让const调用非const版本!
class CTextBlock{
public:
CTextBlock(string s)
{pText = s;}
constchar& operator[](size_t position)const //for const对象!
{returnpText[position];}
char&operator[](size_t position) //for 非const对象! 为了避免代码重复,设法让非const成员函数调用const成员函数。而不是让const调用非const版本!
{
return
const_cast<char&>( //返回值去掉const属性
static_cast<constCTextBlock&>(*this)[position]//为*this(当前对象)加上 const 再调用 const op[]
);
}
private:
string pText;
};
int main()
{
CTextBlock cctb("Hello");
}
条款4 确定对象被使用前已被初始化
(0)
永远在使用对象之前将它初始化。为内置型对象进行手工初始化,因为c++不保证初始化它们。
(1)
class PhoneNumber{...};
class ABEnter{
public:
ABEnter(conststring& name,
conststring& address,
const list<PhoneNumber>& phones);
private:
string theName; //class的成员变量的初始化顺序是依照声明的次序
string theAddress;
list<PhoneNumber> thePhones;
int numTimesConsulted;
};
//第一种构造函数:不使用成员初值列
ABEnter::ABEnter(conststring& name,
conststring& address,
const list<PhoneNumber>& phones)
{
theName = name; //这些都是赋值而不是初始化
theAddress = address; //C++规定:对象的成员变量的初始化动作发生在进入构造函数本体之前
thePhones = phones; //这个版本首先调用默认构造函数设初值,再立刻对他们赋予新值
numTimesConsulted =0;
}
//第二种构造函数:使用成员初值列
ABEnter::ABEnter(conststring& name,
conststring& address,
const list<PhoneNumber>& phones)
:theName(name), //成员初值列,现在这些都是初始化
theAddress(address), //这些参数被直接作为构造函数的实参。
thePhones(phones), //比如当前行,thePhones以address为初值进行copy构造
numTimesConsulted(0)
{} //构造函数本体不必有任何动作
构造函数一定是使用 成员初值列!而不是在本体中使用赋值操作。初值列列出的成员变量,其排序次序应该和它们在class中的声明次序相同。
总在初值列中列出所有成员变量,以免还得记住哪些成员变量无需初值。
(2)解决初始化次序不确定性。
为免除“跨编译单元之初始化次序”问题,用local static 代替 non-local-static。调用可直接使用tfs(),
因为c++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。
class FileSystem{
public:
...
size_t numDisks() const;
...
};
FileSystem& tfs()//如果不是一个函数而是直接extern FileSystem tfs;,在另一个文件中的Directory类对象的构造函数需要这个文件中的tfs对象,但是这个tfs可能尚未初始化。
{//因为是定义域不同编译单元内的non-local static对象。如何才能确定tfs在tempdir之前被初始化?是无法确定的。
//所以说将non-lcoal static搬到自己的专属函数内(对象在此函数内被声明为static)。然后用户调用这些函数,而不直接指涉这些对象。是Singleton模式的一个常见手法。
staticFileSystem fs; //static修饰局部变量。保证只有在第一次调用时才初始化创建。
return fs;
}
//下面的内容在另一个文件中
class Directory{...};
Directory::Directory(param)
{
...
size_t disks = tfs().numDisks();
...
}
Directory& tempDir()
{
staticDirectory td;
return td;
}
- 【读书笔记】Effective C++-1 让自己习惯C++(之一)
- Effective C++(一)让自己习惯C++
- Effective C++(一)让自己习惯C++
- effective c++-让自己习惯c++
- 《Effective C++》第一章:让自己习惯C++
- 《Effective C++》1-让自己习惯C++
- 【读书笔记】Effective C++-1 让自己习惯C++(之二)
- 【读书笔记】Effective C++-1 让自己习惯C++(之三)
- 《Effective C++》让自己习惯C++:条款1-条款4
- Effective C++之一:让自己习惯C++
- Effective C++读书笔记1(让自己习惯C++)
- Effective C++读书笔记1(让自己习惯C++)
- Effective C++(一)让自己习惯C++
- 《Effective C++》 读书笔记(一) 让自己习惯C++
- 【Effective C++】条款01-让自己习惯c++
- Effective C++ 第一章(让自己习惯C++)
- Effective C++学习笔记一(让自己习惯C++)
- <<Effective C++>>读书笔记1: 让自己习惯C++
- ActiveReports 报表控件V12新特性 -- RPX报表转换为RDL报表
- SpringBoot继承TkMapper通用Mapper
- 二叉搜索树
- ViewPager轮播图引导页布局
- Delphi整理一(基础知识)
- Effective C++之一:让自己习惯C++
- SQL创建临时表
- push本地代码到github出错
- MVP解绑
- 2级列表
- ModuleNotFoundError: No module named 'xgboost',windows下初安装xgboost
- C++面向对象之引用
- 深度神经网络为何很难训练?
- 线程通讯和同步的两种实现方…