您的位置:首页 > 编程语言 > C语言/C++

C++ Primer阅读心得(第十四章)

2008-04-09 01:15 281 查看
1.不能重载的操作符,包括:  

: : (域操作符) 
.* (指针成员操作符)(这货很少用到啊)
 .(点操作符)  
?: (问号表达式)

2.不建议重载的操作符,包括:

因为无法保证求值顺序,不应该重载:逻辑与&&、逻辑或||、逗号运算符,
重载operator&之后会造成很多问题(尤其是返回类型与默认返回类型不一致时),所以避免重载operator&。

3.操作符重载的基本规则:

函数名称为:operator加上操作符
有返回类型、函数体和参数列表
一元操作符一个参数,二元操作符两个参数
成为类成员的操作符重载第一个参数被默认为this,所以参数个数再减一
只有调用操作符()可以有参数默认值,其他都不可以
操作符重载函数或者是类成员,或者必须含有一个类类型的参数(所以你不能重载内置类型的操作符)
只能重载已经存在的操作符,不能创造新的操作符(废话)
重载操作符的优先级和结合律与默认的保持一致

是不是很啰嗦?在这里举个例子吧。

class Foo {
friend Foo operator+(const Foo&, const Foo&);
public:
explicit Foo(int i):data(i){}
private:
int data;
}

Foo operator+(const Foo &left, const Foo &right) {
return Foo(left.data + right.data);
}


4.下面将各种操作符的重载列在如下表中:

操作符名称函数名返回值参数列表声明位置其他说明
输入输出操作符operator<<

operator>>
stream对象的引用

(保证>>和<<可以连续使用)
stream &s, const Type &t

(输入输出会改变流状态,所以引用不能为const)
类外friend

(因为第一个参数不能为this)
输入操作符必须处理流失败

输出操作符则不需要
算数操作符operator+

operator-

operator*

operator/

%^!~&|
Type的临时对象

(与内置类型的行为保持一致)
const Type &t1, const Type &t2类外friend推荐连同对应的复合运算符

一起定义,在内部可以使用

定义好的复合运算符来实现

对应的外部运算符
关系运算符operator==

operator!=

operator<

<= > >=
boolconst Type &t1, const Type &t2类外friend推荐成对定义,例如:定义

了==后把!=也定义了,并用

==实现!=;推荐定义<,因

为这样可以在更多的STL容

器中使用;最好能够保障传

递性
赋值运算符operator=Type &

(返回左侧对象的引用)
const Type & (拷贝赋值)

Type && (移动赋值)

以及其他各种类型
类member

(因为要返回左侧对象的引用)
只建议定义拷贝、移动以及

initializer_list版本的,其他

类型的版本容易让人费解
复合赋值运算符operator+=

operator-=

operator*=

operator/=

%=^=&=|=

<<= >>=
Type &const Type & 

以及其他各种类型
推荐类member不建议定义奇怪的,定以后

建议同时定义对应的运算
下标运算符operator[]类内对象类型的引用size_t类member

(因为要返回类内对象的引用)
建议同时定义const与非

const版本,来保证const

对象也可以使用
前置递增递减operator++

operator--
Type &类member

(因为改变了对象本身)
定义前置的同时建议也定

义后置版本
后置递增递减operator++

operator--
Type的临时对象

(递增递减前的拷贝)
int

(注意,int只是为了与前置版本区分,从来不用)
类member定义后置的同时建议也定

义前置版本
成员访问运算符operator*

operator->
Type &

Type *
类member推荐成对定义:注意保证->

的语义不变
函数调用运算符operator()任意任意类member

(不然就不是函数对象了)
可以有多个重载版本
类型转换运算符operator typetype类member注意类型转换的二义性
5.lambda表达式与函数对象:定义了函数调用操作符(operator())的类型的对象被称为函数对象,它们可以被调用函数那样子调用。
class Foo{
public:
int operator() (int a, int b) const {
return a - b;
}
};

Foo minus; //函数对象
int c = minus(5, 4); //c = 1
而lambda表达式的背后,由编译器生成了一个重载了operator()的临时类的临时对象。值捕获的局部变量会被临时类的构造函数“拷贝”到类中;非mutable标记的lambda表达式对应的operator()是const的(所以不能修改捕获的变量);引用捕获的局部变量就不需要这一“拷贝”过程,而直接被编译器使用。lambda表达式和函数对象通常与泛型算法合作完成任务。

6.function类型:在c++当中,以下四种类型的对象是可以被调用的。

函数
函数指针
lambda表达式
可调用对象

在混合使用它们的时候,相当的不方便,因为它们是四种不同类型的对象。为了解决这一问题,c++11中新增了function类型模板,以调用形式(“返回值-参数列表”)为Type,可以兼容以上全部类型,让它们像同一个类型的对象一样工作。
int add(int a, int b) {
return a + b;
}
int (*add_ptr) (int, int) = add;
auto add_lambda = [](int a, int b) {
return a + b;
}
class ADD {
int operator() (int a, int b) {
return a + b;
}
};
ADD add_object;
function<int(int, int)> f1 = add;
function<int(int, int)> f2 = add_ptr;
function<int(int, int)> f3 = add_lambda;
function<int(int, int)> f4 = add_object;

注意:不能将重载函数直接放入function中,需要用函数指针明确指明或者lambda临时包装一下(这方法太经典了)。

7.显式类型转换符explicit:在c++11中,新增了显式类型转换符,用来阻止编译器利用定义好的类型转换符来进行隐式类型转换。增加了explicit的类型转换符必须显式调用类型转换才可以(static_cast等)。

class Foo {
public:
Foo (int i):data(i){}
explicit operator int () {return data;}
private:
int data;
};
Foo f(1);
int a = 2 + f; //隐式转换,错误
int b = 2 + static_cast<int>(f); //ok,显式转换
注意:当表达式被当做条件处理时(if/while/do-while/for/问号表达式/&&/||/!),编译器会隐式的执行显式类型转换。

8.类型转换的best practice:除了定义显式bool类型转换符之外,不要定义其他的类型转换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息