2 minute read

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(隐式).