C++是多重范型编程语言,同时支持过程形式,面向对象形式,函数形式,泛型形式,元编程形式(什么是元编程?)的语言。
C++主要包括4部分:C, Object-Oriented C++, Template C++和STL。
1, 对于单纯变量最好以const对象或enums替换#defines.
#define ASPECT_RATIO 1.653
const double ASPECT_RATION = 1.653;
常量定义式通常放在头文件中,若要在头文件内定义一个常量的char * -- based 字符串:
const char * const autherName = “Scott Meyers”;
Or
const std::string autherName(“Scott Meyers”);
Class专属常量:
class GamePlayer{ Private: static const int NumTurns = 5; int scores[NumTures]; … };
Const int Gameplayer::NumTurns; |
class GamePlayer{ Private: static const int NumTurns; Int scores[NumTures]; … };
Const int Gameplayer::NumTurns = 5;//位于实现文件内 |
若在编译期间需要一个class常量值,可以通过所谓的”the enum hack”补偿做法。
class GamePlayer{
Private:
enum {NumTurns = 5};
int scores[NumTures];
…
};
2, 对于形似函数的宏,最好改用inline函数替换#defines.
#define CALL_MAX(a, b) f((a) > (b) ? (a) : (b))
int a = 5, b = 0;
CALL_MAX(++a, b); // a被累加二次
CALL_MAX(++a, b + 10) // a被累加一次
可以替换成:
template<typername T>
inline void CALL_MAX(const T& a, const T& b) //T未知, 采用pass-by-ref-to-const, See条款20.
{
f(a > b ? a : b);
}
1, const基本用法
const语法: 若const出现在星号左边,表示被指物是常量;出现在星号右边,表示自身是常量;出现在两边,都是常量。
char greeting[] = “Hello”;
char * p = greeting; //non-const pointer, non-const data
const char * p = greetring; //non-const pointer, const data
char * const p = greeting; //const pointer, non-const data
const char * const p = greeting; //const pointer, const data
如下两种写法意义相同:
void f(const widget * pw) ßà void f(widget const * pw); // pw指向一个常量的widget对象
2, const迭代器
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();
*iter = 10;
iter++; //error, iter is const
std::vector<int>:: const_iterator cter = vec.begin();
*cter = 10; //error, *cter is const
cter++;
令函数返回一个常数值,可以降低因客户错误而造成的意外。
class Rational{}
const Rational operator* (const Rational& ls, const Rational& rhs);
Rational a, b, c;
(a * b) = c; //error
3, const成员函数
class TextBlock{
const char& operator[] (std::size_t pos)const //operator for const对象
{return text[pos];}
char& operator[] (std::size_t pos) //operator for non-const对象
{return text[pos];}
private: std::string text;
};
TextBlock tb(“Hello”);
std::cout<<tb[0]; //调用non-const TextBlock::operator[]
tb[0] = ‘x’; //OK
const TextBlock ctb(“world”);
std::cout<<ctb[0];//调用const TextBlock::operator[]
ctb[0]=’x’; //error! 写一个const TextBlock
成员函数只有在不改变任何成员变量时才可以说是const。许多成员函数虽然不十足具备const性质,但能通过编译测试。
class TextBlock{
char& operator[] (std::size_t pos)const //operator for const对象
{return text[pos];}
private: std::string text;
};
const TextBlock ctb(“Hello”);
char * pc = &ctb[0];
*pc = ‘J’; //ctb现在是Jello
class TextBlock{ std::size_t length()const private: std::string text; std::size_t textLen; }; std::size_t TextBlock::length()const { textLen = text.len(); //error,不能给textLen赋值 return textLen; } |
class TextBlock{ std::size_t length()const private: std::string text; mutable std::size_t textLen; }; std::size_t TextBlock::length()const { textLen = text.len(); return textLen; } |
4, 在const和non-const成员函数中避免重复
class TextBlock{ const char& operator[](std::size_t pos) const { …//边界检验 …//数据访问 …//检验数据完整性 }
char& operator[](std::size_t pos) { …//边界检验 …//数据访问 …//检验数据完整性 }
private: std::string text; }; |
class TextBlock{ const char& operator[](std::size_t pos) const { …//边界检验 …//数据访问 …//检验数据完整性 }
char& operator[](std::size_t pos) { return const_cast<char &> ( static_cast<const TextBlock&> (*this) [pos] ); }
private: std::string text; }; |
综上:常成员函数 <类型标志符> 函数名(参数表)const;
1,const是函数类型的一部分,在实现部分也要带该关键字。
2,const关键字可以用于对重载函数的区分。
3,常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数和常数据成员。
4,常成员函数可以被其他成员函数调用。
5,但是不能调用其他非常成员函数。
6,可以调用其他常成员函数。
1, 构造函数最好使用成员初值列,而不要在构造函数体内使用赋值操作。初值列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
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> thephones;
int numTimesConsulted;
};
ABEntry: :ABentry(const std::string& name,
const std::string& address,
const std::list<PhoneNumber>& phones);
{
thename = name; //这些都是赋值,不是初始化
the address = address;
the phones = phones ;
numTimesConsulted = 0;
}
C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。在ABentry构造函数内,theName, theAddress和thePhones都不是初始化,而是赋值。初始化的发生时间更早,发生于这些成员的default构造函数被自动调用之时。然后立刻对它们赋予新值,因此default构造函数的一切作为浪费了。
//测试代码: class PhoneNumber { public: PhoneNumber(){cout<<"PhoneNumber default ctor"<<endl;} PhoneNumber(PhoneNumber& ){cout<<"PhoneNumber copy ctor"<<endl;} PhoneNumber& operator = (PhoneNumber&) {cout<<"PhoneNumber operator ="<<endl;return *this;} };
class TextBlock{ public: //TextBlock(PhoneNumber& _pn):pn(_pn){} TextBlock(PhoneNumber& _pn){ cout<<"----"<<endl; pn = _pn; } private: PhoneNumber pn; };
PhoneNumber pn; int main() { cout<<"####"<<endl; TextBlock tb(pn); }
//输出: PhoneNumber default ctor #### PhoneNumber default ctor ---- PhoneNumber operator = |
因此,下面的构造构造函数效率比上面的要高。
ABEntry: :ABentry(const std::string& name,
const std::string& address,
const std::list<PhoneNumber>& phones)
:thename (name), the address(address), the phones(phones), numTimesConsulted(0) //这些都是初始始化
{ }
技巧: 总是在初值列中列出所有成员变量,以免还得记住哪些成员变量可以无需初值。
C++有着十分固定的“成员初始化次序”。是的,次序总是相同:base classes更早于derived classes初初始化,而class的成员变量总是以其声明次序被初始化。即thename成员永远最先初始化,然后是theaddress, 再来是thephones,最后是numTimesConsulted。
2, 为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。
注: 函数内的static对象称为local static对象,其他static对象称为non-local static对象。编译单元是指产出单一目标文件的那些源码。基本上它是单一源码文件加上其所含入的头文件。
现在关心的问题涉及至少两个源码文件,每一个内含至少一个non-local static对象(也就是说该对象是global或位于namespace作用域内,抑或在class内或file作用内被声明为static)。
class FileSystem{
public:
…
std::size_t numDisks()const;
…
};
extern FileSystem tfs;
现在假设某些客户建立了一个class用以处理文件系统内的目录。
class Directory{
public:
Directory(params);
…
};
Directory::Directory(params)
{
…
std::size_t disks = tfs.numDisks(); //使用tfs对象
…
}
Directory tempDir(params);
显然,除非tfs在tempDir之前先初始化,否则tempDir的构造函数会用到尚未初始化的tfs。但tfs和tempDir是不同人不同时候创建的,定义于不同编译单元内的non-local static对象。如何确定tfs会在tempDir之前先被初始化?
C++对“定义于不同编译单元内的non-local static对象”的初始化相对次序并无明确定义。
解决方法:
class FileSystem{…}
FileSystem& tfs()
{
static FileSystem fs;
return fs;
}
class Directory{ …};
Directory::Directory(params)
{
…
std::size_t disks = tfs().numDisks(); //使用tfs对象
…
}
Directory & tempDir()
{
static Directory td;
return td;
}