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也可实现字符串到数字的转换