C++98/03拾遗

const关键字

常量修饰符

const变量不再允许延迟赋值,必须于声明时赋值。

1
const int MAX_VALUE; // ERROR!

而下例是允许的,因为其是指向常整型的指针变量。

1
const int* pt;

而指向整型变量的常指针的声明如下。

1
int *const pt;

对于迭代器而言则如下。

  • const iterator允许改变迭代器所指之物的值,但不允许指向其他东西;

    1
    ++it; // ERROR!
  • const_iterator不允许改变迭代器所指之物的值,但允许指向其他东西。

    1
    *cIt = 0; // ERROR!

const成员函数

Physical Constness

不得更改对象之任何非static成员变量,又称Bitwise Constness。

Physical const is stronger than logical const; if an operation is physically const it is also logically const.

Logical Constness

允许在客户端侦测不出得情况下修改对象。

mutable是与const成员函数相关的摆动场,用于声明允许const成员函数更改的成员变量。

1
2
3
private:
mutable std::size_t textLength;
mutable bool lengthIsValid;

const成员函数和非const成员函数有着实质等价的实现时,令非const版本调用const版本以避免代码重复。

数组的引用

C++中没有「引用的数组」(array of reference),形如int&a[N]将非法。

ERROR: declaration of 'a' as array of references.

但有“数组的引用”,形如int(&a)[N],其作用之一是防止数组退化为指针。

下例的&是必须的,否则会产生编译期错误,原因是纯粹的auto推断使引用信息丢失。

1
2
3
4
5
6
7
int a[2][3] = { 0, 1, 2, 3, 4, 5 };
for(auto & i : a) {
for(auto j : i) {
cout << j << ' ';
}
}
cout << '\n';

「数组的引用」还能与模板一同发挥作用。

1
2
3
4
template<typename T, int N>
void sort(T(&a)[N]) { ... }
...
sort(a);

复制和赋值

1
2
3
Box b1;
Box b2 = b1; // 复制,Box(const Box &)
b2 = b1; // 赋值,operator=(const Box &)

指向成员函数的指针

通过&创建,访问时使用.*->*

下例中&A::func&有时能省略。而在Visual Studio 2015/2017中,省略&将会报错。

A::func:非标准语法。请使用&来创建指向成员的指针。

1
2
3
4
5
A a;
A* pa = &a;
void (A::*p)() = &A::func;
(t.*p)();
(pt->*p)();

共有友元函数

1
2
3
4
5
6
7
8
class B; // 必须前置声明
class A {
public: friend void f(A & a, B & b);
};
class B {
public: friend void f(A & a, B & b);
};
void f(A & a, B & b) { ... }

特殊的运算符重载

仿函数(Functor; operator())

又称伪函数。使用仿函数的示例见

operator newnew

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A {
public:
A() { cout << "A()\n"; }
~A() { cout << "~A()\n"; }
void* operator new(size_t size) {
cout << "operator new()\n";
return malloc(size);
}
void operator delete(void* p) {
cout << "operator delete()\n";
free(p);
}
};

int main(void) {
A* p = new A;
delete p;
return 0;
}

输出如下。

1
2
3
4
operator new()
A()
~A()
operator delete()

总结如下。

new = operator new + 构造;delete = 析构 + operator delete

两种类型的operator[]

1
2
3
4
// const对象
const char & operator[](std::size_t i) const { return str[i]; }
// 非const对象
char & operator[](std::size_t i) { return str[i]; }

避免代码重复的操作如下。

1
2
3
4
5
6
// 非const对象
char & operator[](std::size_t i) {
return const_cast<char&>( // 移除const修饰
static_cast<const Text &>(*this)[i] // 调用const对象的operator[]
);
}

自增/自减运算符

1
2
3
4
5
6
7
8
9
A & A::operator++() { // 前缀
...
return *this; // 前缀返回可修改的引用
}
const A A::operator++(int) { // 后缀,此处的int用于区分
A temp(*this);
...
return temp; // 后缀返回不可修改的值
}

流运算符

返回引用是因为istreamostream不允许拷贝。

1
2
friend istream & operator>>(istream & in, A & a);
friend ostream & operator<<(ostream & out, const A & a); // 注意此处的const

其他

不允许重载的运算符有成员访问运算符.、成员指针访问运算符.*、域运算符::、三元条件运算符?:sizeof运算符。

重载箭头运算符operator->的方式如下。

1
A* operator->() { return this; }

重载&&||运算符,并不能复现短路求值的规则。

定位new(Placement new)

需包含如下头文件。

1
#include <new>

在已分配的内存空间上实施构造,释放时不使用delete

1
2
3
4
5
6
7
8
9
10
11
12
class A {
public:
int n = 0;
};

int main(void) {
auto a = (A*)malloc(sizeof(A));
new(a) A;
cout << a->n << '\n';
free(a);
return 0;
}

模板的实例化和特化

  • (显式/强制)实例化;

    1
    template class A<TYPE>;
  • (显式)特化/具体化。

    1
    template<> class A<TYPE> { ... };

谓词(Predicate)

谓词共计三类如下。

  • 函数/函数指针谓词;
  • lambda表达式谓词;
  • 仿函数谓词。

带捕获列表的lambda不可以隐式转换为函数指针。

1
void (*ptr)(int) = [&](int) { }; // ERROR!

统一三类谓词的函数参数传递方式如下。

  • 标准库实现;

    1
    2
    template<typename Pred>
    void fun(Pred p) { ... }
  • 使用function

    需包含如下头文件。

    1
    #include <functional>
    1
    void fun(function<bool(...)> p) { ... }

使用function获取类成员函数如下例。

下例的const仅当fconst函数时需要,而f为类静态成员函数时,则不需要cosnt A &类型的第一个参数。

1
function<void(const A &, ...)> func = &A::f;

数字—字符串的转换

需包含如下头文件。

1
#include <sstream>

数字转字符串如下例。

若重复利用同一个stringstream对象,需要调用clear(),否则将引发错误。

1
2
3
4
stringstream ss;
string s;
ss << 100;
ss >> s;

stringstream也可实现字符串到数字的转换。