C++复习笔记
2020年 02 月 26 日更新
变量初始化
函数体之外的变量都被初始化为 0 函数体内部的变量都不会被初始化
- 直接初始化 没有等号
- 拷贝初始化 有等号
引用
引用一旦被初始化后, 就不能改为其他对象, (会进行赋值操作)
传递引用
可以阻止函数传递时, 对参数的拷贝
类
成员函数
Type A::f() const
{}
若将成员函数声明为const,则不允许通过其修改类的数据成员。 值得注意的是,如果类中存在指针类型的数据成员即便是const函数只能保证不修改该指针的值,并不能保证不修改指针指向的对象。
设置友元类, 可以访问 私有变量
构造函数初始值
在构造函数中,无法忽略 初始化和赋值之间的差异
- 构造函数初始化存在顺序关系 最好让保证于成名声明的顺序一致
class TestA {
public:
TestA(int ii);
private:
int i;
const int ci;
int &i;
}
//如使用赋值的话, 会出现错误
/*
TestA::TestA(int ii) {
i = ii;
ci = ii; // const 无法被赋值
ri = i; // ri 没有被初始化
}
*/
// 正确的是 (构造方式)
TestA::TestA(int ii):i(ii), ci(ii), ri(i) {}
析构函数
类中有且仅有一个析构函数
类设计
C++ 并不要求一定要自己定义析构函数, 拷贝构造函数, 拷贝赋值运算符, 但是这些操作应该被视为一个整体, 一般不会出现只定义某一个函数的情况
错误示范
class A{
public:
A(const string &s = string()):ps(new string(s)), i(0) { }
~A() {delete ps;}
//
};
A f(A hp) {
A ret = hp; // 拷贝
return ret; // ret 和 hp 都被销毁
}
// 如果有如下使用
A p("hi");
f(p); //p.ps指向的内容被释放
A q(p); // q, p 指向无效内存
合成的xx函数
- 具有引用成员或无默认构造的const成员的类, 编译器不会为其合成默认构造函数.
容器
swap
除了string外, 指向容器的迭代器,引用, 指针 不会 失效, 仍然指向之前的值.
只有Array 真正交换了他们的元素.
bind
bind 函数拷贝其参数, 如果提供一个对象但不拷贝他, 需要使用标准库的ref函数
函数
尾置返回类型
auto func(int i) -> int(*)[10]
用以简化函数的返回值
迭代器
普通迭代器
*it;
++it;
it++
不会对 迭代器 it 做任何事情, 每个操作都会返回it
iostream 迭代器
istream_iterator
istream_iterator<int> int_it(cin); // 从cin 读int
istream_iterator<int> int_eof; // 尾后迭代器 (用来判断读取中止)
ostream_iterator
vector<string> test(10, "test");
// 将类型T写到cout 流中, 并且中间以---隔开
ostream_iterator<string> out_iter(cout, "---");
copy(test.begin(), test.end(), out_iter);
cout << endl;
算法中使用的迭代器错误, 可能不会被语法检查器检查出来.
编程提示
- 如果两个对象共享底层的数据, 当某个对象被销毁的时候, 不能单方面的销毁底层数据
指针
string *ps1 = new string; // 默认初始化 空string
string *ps2 = new string(); // 值初始化 空string
int *pi1 = new int; // 默认初始化 未定义
int *pi2 = new int(); // 值初始化 *pi2 为0
空悬指针的问题
int *p(new int(42));
auto q = p;
delete p; // p, q 均无效
p = nullptr; // q 仍然是空悬指针
智能指针 + 动态指针
shared_ptr<int> p1 = new int(1024); // 错误
shared_ptr<int> p2(new int(1024)); // 正确
智能指针 + 普通指针
void process(shared_ptr<int> ptr) { }
int *x(new int(1024));
process(x); // 错误, 无法将int*类型转换到shared_ptr<int>
process(shared_ptr<int>(x)); // 合法
int j = *x // 未定义的, x成为空悬指针, 指向内容已经被智能指针释放
智能指针 + 动态数组
unique_ptr 管理动态数组
unique_ptr<int[]> up(new int[10]);
up.release(); // 自动调用delete[], 销毁其指针
// 与普通的unique_ptr 不同
shared_ptr 不能直接管理动态数组, 如果必须要用, 要提供自己写的删除器
shared_prt<int> sp(new int[10], [](int *p) {delete[] p; });
sp.reset(); // 调用lambda 释放数组
shared_ptr 没有定义下标, 也不支持指针++
new
char arr[0]; // 非法
char *cp = new char[0]; // 合法但是不能被解引用
allocator
// 创建
allocator<string> alloc;
auto const p = alloc.allocate(n); // 传递 n = 3 // string *const p
// 赋值
auto q = p;
alloc.construct(q++); //*q 为空字符串
alloc.construct(q++, 2. 'c'); //*q 为cc
alloc.construct(q++, "hi"); //*q 为hi
// 销毁
while(q != p)
alloc.destroy(--q);
// 返回系统
alloc.deallocate(p, n);
explicit
explicit关键字只能用于修饰 只有一个参数 的类构造函数, 它的作用是表明该构造函数是 显示 的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).