构造函数
class IntCell
{
public:
explicit IntCell(int initialVaule = 0): storedValue{initialValue} {}
int read() const {return storedValue;}
void write(int x) {storedValue = x;}
private:
int storedValue;
};
初始化列表
当数据成员是类时,使用初始化列表而不是在函数体内赋值可以节省时间。而且以下两种情况只能使用初始化列表:
1. 数据成员是const
类型
2. 如果数据成员是个类,这个类没有zero-parameter
构造函数,即默认构造函数,只能使用初始化列表。
在C++11中用花括号代替括号来初始化。
explicit
你应该将所有的只有一个参数的构造函数设成explicit
避免类型转化。
通常一个参数的构造函数定义了一个隐藏的类型转化。
IntCell obj;
obj = 37;
其实是这样的:
IntCell temporary = 37;
obj = temporary;
temporary
的初始化会用到一个参数的构造函数,如果使用了explicit
就可以避免这种转化。
声明
下面是正确的:
IntCell obj1; // Zero parameter constructor
IntCell obj2( 12 ); // One parameter constructor
下面是不正确的:
IntCell obj3 = 37; // Constructor is explicit,要使用()
IntCell obj4( ); // Function declaration
而obj4就是一个函数声明,零参数,返回值是IntCell
,这也是使用花括号初始化的原因之一,虽然初始化列表中可以使用括号初始化零参数,但是有点丑。
IntCell obj1; // Zero parameter constructor, same as before
IntCell obj2{ 12 }; // One parameter constructor, same as before
IntCell obj4{ }; // Zero parameter constructor
右值引用
在C++ 11 以前引用只有左值引用,左值是一个非临时对象。
string str = "hell";
string & rstr = str; // rstr is another name for str
rstr += ’o’; // changes str to "hello"
bool cond = (&str == &rstr); // true; str and rstr are same object
string & bad1 = "hello"; // illegal: "hello" is not a modifiable lvalue
string & bad2 = str + ""; // illegal: str+"" is not an lvalue
string & sub = str.substr( 0, 4 ); // illegal: str.substr( 0, 4 ) is not an lvalue
右值引用由&&
表示:
string str = "hell";
string && bad1 = "hello"; // Legal
string && bad2 = str + ""; // Legal
string && sub = str.substr( 0, 4 ); // Legal
左值引用经常用在下面几个方面:
- 关联复杂的名字
auto & whichList = theLists[ myhash( x, theLists.size( ) ) ];
if( find( begin( whichList ), end( whichList ), x ) != end( whichList ) )
return false;
whichList.push_back( x );
如果只写成:
auto whichList = theLists[ myhash( x, theLists.size() ) ];
witchList
只是一个拷贝,不能使用push_back
之类的操作。
- 在循环中
for( int i = 0; i < arr.size( ); ++i )
++arr[ i ];
上面的代码可以写成:
for ( auto & x: arr )
++x;
- 避免拷贝比较大的对象
例如在函数返回值是个比较大的对象,但是并不改变这个对象,我们可以返回这个对象的引用。
在C++中参数传递通常是值传递。
一般遵循下面的原则:
- 如果是个小对象,在函数中不需要改变,采用值传递(call-by-value)。
- 如果是个比较大的对象,在函数中不需要改变,采用常量引用(call-by-constant-reference)。
- 如果传递的对象需要改变,采用引用(call-by-reference)。
请看下面的例子:
double average( double a, double b ); // returns average of a and b
void swap( double a, double b ); // swaps a and b; wrong parameter types
string randomItem( vector<string> arr ); // returns a random item in arr; inefficient
上面的代码应该改成:
double z = average( x, y );
void swap( double & a, double & b );
string randomItem( const vector<string> & arr );
我们来看关于返回值方面的,下面有三个函数:
double average( double a, double b ); // returns average of a and b
LargeType randomItem( const vector<LargeType> & arr ); // potentially inefficient
vector<int> partialSum( const vector<int> & arr ); // efficient in C++11
将第二个函数进行改变,使之更有效率:
LargeType randomItem1( const vector<LargeType> & arr )
{
return arr[ randomInt( 0, arr.size( ) - 1 ) ];
}
const LargeType & randomItem2( const vector<LargeType> & arr )
{
return arr[ randomInt( 0, arr.size( ) - 1 ) ];
}
vector<LargeType> vec;
...
LargeType item1 = randomItem1( vec ); // copy
LargeType item2 = randomItem2( vec ); // copy
const LargeType & item3 = randomItem2( vec ); // no copy
注意item2
还是进行了拷贝,只有item3
没有拷贝,如果返回值是常量引用,那么变量也要是常量引用才不会拷贝。
来看第三个函数:
vector<int> partialSum( const vector<int> & arr )
{
vector<int> result( arr.size( ) );
result[ 0 ] = arr[ 0 ];
for( int i = 1; i < arr.size( ); ++i )
result[ i ] = result[ i - 1 ] + arr[ i ];
return result;
}
vector<int> vec;
...
vector<int> sums = partialSum( vec ); // Copy in old C++; move in C++11
在C++ 11中,如果返回的是一个右值,会使用move
来提高效率。
来看第一个函数:
void swap( double & x, double & y )
{
double tmp = x;
x = y;
y = tmp;
}
void swap( vector<string> & x, vector<string> & y )
{
vector<string> tmp = x;
x = y;
y = tmp;
}
因为x
,y
都是左值,所以不会像第二个函数一样用move
进行传递。但是我们可以使用cast
来将右边的值看做右值:
void swap( vector<string> & x, vector<string> & y )
{
vector<string> tmp = static_cast<vector<string> &&>( x );
x = static_cast<vector<string> &&>( y );
y = static_cast<vector<string> &&>( tmp );
}
但是static_cast
的语法比较复杂,标准库中的函数std::move
可以实现:
void swap( vector<string> & x, vector<string> & y )
{
vector<string> tmp = std::move( x );
x = std::move( y );
y = std::move( tmp );
}
未完,在学习中不断地增加。。。。