Featured image of post More Effective C++ 02 - Operators

More Effective C++ 02 - Operators

弄清每个符号的功能

# Be wary of user-defined conversion functions

C++ 允许编译器在不同类型之间执行隐式转换。

两种函数允许编译器执行转换: 单自变量 constructors 和隐式类型转换操作符,此类 constructor 可能声明拥有单一参数,也可能声明拥有多个参数,并且除了第一参数以外都有默认值。

1
2
3
4
5
6
7
8
9
class Name {
public:
    Name(const string& s); // 可以把 string 转换成 Name
};

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1); // 可以把 int 转换成 Rational
};

隐式类型转换操作符是一个拥有特殊名称的成员函数: 关键词 operator 后面加上一个类型名称。

1
2
3
4
5
6
class Rational {
public:
    operator double() const; // 将 Rational 转换成 double
};
Rational r(1,2);
double d = 0.5 * r; // 将 r 转换成 double 再进行乘法运算

为了解决上述隐式转换的问题: 以功能对等的另一个函数取代类型转换操作符。

1
2
3
4
5
6
7
8
class Rational {
public:
    double asDouble() const;
};

Rational r(1, 2);
cout << r; // error, Rational 没有 operator <<
cout << r.asDouble(); // ok, 以 double 形式输出 r

解决单自变量 constructors 的隐式转换带来的问题: 使用关键字 explicit。只要将 constructor 声明为 explicit,编译器便不能因隐式类型转换的需要而进行调用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
template <class T>
class Array {
public:
    Array(int lowBound, int highBound);
    Array(int size); // 单自变量constructor => replace explicit Array(int size);
    T& operator[](int index);
};

bool operator==(const Array<int>& lhs, const Array<int>& rhs);

Array<int> a(10);
Array<int> b(10);
for (int i = 0; i < 10; ++i) {
    if (a == b[i]) { // a 应该是 a[i]
    // 上述代码相当于 if (a == static_cast<Array<int>>(b[i]))
      cout << "index " << i << " is equal" << endl;
    } else {
      cout << "index " << i << " is not equal" << endl;
    }
}

# Distinguish between prefix and postfix forms of increment and decrement operators

  • prefix: increment and fetch
  • postfix: fetch and increment
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class UPInt {
public:
    UPInt& operator++(); // prefix
    const UPInt operator++(int); // postfix

    UPInt& operator--();
    const UPInt operator--(int);

    UPInt& operator+=(int);
};

UPInt& UPInt::operator++() {
  *this += 1;
  return *this;
}

const UPInt UPInt::operator++(int) {
  UPInt oldValue = *this;
  ++(*this);
  return oldValue;
}

UPInt i;
i++++; // 同下
i.operator++(0).operator++(0); // 由于返回值是 const, 此操作不合法

设计 classes 的一条无上宝典就是: 一旦有疑虑,查看 ints 行为如何并遵循之。

# Never overload &&, || or .

C++ 对于 “true or false” 采用的评估方式是 “fail fast”。一旦重载了 operator ||operator &&,函数调用语义会替代 “fail fast”。

  1. 当函数调用动作被执行,所有参数值都必须评估完成
  2. C++ 语言规范并未明确定义函数调用动作中各参数的评估顺序

表达式如果包含逗号,那么逗号左侧会被先评估,然后逗号的右侧再被评估;最后,整个逗号表达式的结果以逗号右侧的值为代表。

不能重载的操作符: ., .*, ::, ?:, new, delete, sizeof, typeid, static_cast, dynamic_cast, const_cast, reinterpret_cast

# Understand the different meanings of new and delete

new operator 是语言内建的,不能改变意义

  1. 分配足够的内存,用来放置某类型的对象
  2. 调用一个 constructor,为刚才分配的内存中的那个对象设定初值

new operator 调用某个函数,执行必要的内存分配动作,我们可以重写或重载那个函数,改变其行为,其函数名为 operator new

1
2
3
4
5
6
7
string *ps = new string("a"); // new operator

void* operator new(size_t size);

void* raw_memory = operator new(sizeof(string));
call string::string("abc") on *raw_memory;
string* ps = static_cast<string*>(raw_memory);
  • 如果希望将对象产生于 heap,可以使用 new operator
  • 如果只是打算分配内存,可以使用 operator new
The older I get, the more I realize that most of life is a matter of what we pay attention to, of what we attend to [with focus].
Built with Hugo
Theme Stack designed by Jimmy