c++2.0

C++ 2.0

nullptr 空指针

void f(int);
void f(void*);

f(0); // 调用 1
f(NULL); // 模糊不定,报错
f(nullptr); // 调用 2

auto

自动推导类型

推导变量类型

auto i = vec.begin(); // 编译器推导出迭代器类型,免去用户手写

推导函数返回值

auto fun() -> decltype(var) {
    //...
}

Unifrom Initialization

旧版的赋值

Rect r1 = { 3, 4, 65 };
Rect r1(3, 4, 65);
int arr[4] = { 11, 23, 65, 32};

新版的列表初始化

int arr[] {1,2,3,4};
vector<int> v {1,2,3,4,5};
vector<string> cities {"New York", "London", "Cairo"};
complex<double> c {4.0, 3.0}; // 等于 c(4.0, 3.0)

编译器一旦检测到 {1, 2, 3, 4}类似的事物,则立刻做出一个 initializer_list<T> 它关联到一个 array<T, n> 。调用函数时(如 ctor)该array内的元素可被编译器分解逐个传给函数。在构建模板对象时调用接受 initializer_list 类型的 ctor。

Initializer_list

接受数量可变的

int i;    // undefined
int j{};  // 0
int* p;   // undefined
int* q{}; // nullptr

int x(5.3); // 数据窄化为 5
int x = 5.3; // 窄化为 5
int x{5.3};  // ERROR: narrowing
int x = {5.3}; // ERROR: narrowing
char x{7};   // OK
char x{9999}; // error
vector<int> v {1,2,3,4,5}; // OK
vector<int> v {1.1, 2.2, 3.3}; // error

作为函数参数

void print(initializer_list<int> vals) {
    for (auto p = vals.begin(); p != vals.end(); ++p) {
        cout << p;
    }
}

print({12, 3, 5, 3});

实现

template <class _E>
class initializer_list {
public:
    typedef _E  value_type;
    typedef const _E& reference;
    typedef const _E& const_reference;
    typedef size_t size_type;
    typedef const _E* iterator;
    typedef const _E* const_iterator;
    
private:
    iterator _M_array;
    size_type _M_len;
    // 编译器在遇到初始化列表时可以调用此构造方法
    constexpr initializer_list(const_iterator __a, size_type __l)
         : _M_array(__a), _M_len(__l) {}
public:
    constexpr initializer_list() noexpect
        : _M_array(__a), _M_len(__l) {}
    
    constexpr size_type
    size() const noexcept { return _M_len; }
    
    constexpr const_iterator
    begin() const noexcept { return _M_array; }
    
    constexpr const_iterator
    end() const no except { return begin() + size(); }
    
    //...
};

explicit

用于构造函数前,保证不会调用被explicit修饰的构造函数进行隐式类型转换。C++11前只能用于一个实参的ctor,之后可以用于不止一个实参的ctor

struct Complex {
    int real, imag;
    Complex(int re, int im=0) : real(re), image(im) {}
    
    Complex operator+(const Complex& x) {
        return Complex((real + x.real), (imag + x.imag));
    }
};

Complex c1(12, 5);
Complex c2 = c1 + 5; // 此处会调用隐式类型转换,将5转为Complex类型。使用explicit修饰后则不会隐式转换

=default =delete

big five:(当对象内部含有指针指向其他对象时,需要重写big five)

  1. 默认构造函数(不带参数的构造函数)
  2. 析构函数
  3. 赋值函数(operator=)(按字节拷贝)
  4. 拷贝构造函数(按字节拷贝)
  5. 移动构造函数
  6. 移动赋值操作

如果自定义ctor,那么编译器不再提供default ctor。当强制使用=default后重新获得default ctor。

class Zoo {
public:
    Zoo(int i1, int i2) : d1(i1), d2(i2) {}
    
    Zoo(const Zoo&) = delete;
    Zoo(Zoo&&) = default;
    Zoo& operator=(const Zoo&) = default;
    Zoo& operator=(const Zoo&&) = delete;
    
    virtual ~Zoo() {}
private:
    int d1, d2;
};

=default 只能用于 big five 等编译器提供默认实现的方法上
=delete 可以用于任何方法
=0 只能用于 virtual 修饰的方法

几种特殊结构

struct NoCopy {
    NoCopy() = default;
    NoCopy(const NoCopy&) = delete;
    NoCopy& operator=(const NoCopy&) = delete;
    ~NoCopy() = default;
};
struct NoDtor {
    NoDtor() = default;
    ~NoDtor() = delete;
};
NoDtor nd; // ERROR 没有dtor 局部变量无法销毁
NoDtor *p = new NoDtor(); // 无法销毁
delete p; // ERROR
// 只允许 friend 或自己成员进行copy
class PrivateCopy {
private:
    PrivateCopy(const PrivateCopy&);
    PrivateCopy& operator=(const PrivateCopy&);
public:
    PrivateCopy() = default;
    ~PrivateCopy();
};

Alias Template

template<typename T>
using Vec = std::vector<T, MyAlloc<T>>;
// 使用
Vec<int> coll; // 等价于 vector<int, MyAlloc<int>> coll;
template<typename Container>
void test_moveable(Container c) {
    // 通过萃取机能够在使用模板作为模板参数时获得参数的参数
    // 如当 Container 为 Vector<string> 时,获得类型 string
    typedef typename iterator_traits<typename Container::iterator>::value_type Valtype;
    Valtype val;
}

alias type

using func = void(*)(int, int); // 等价于 typedef

Template template parameter

template <typename T,
          template<class> 
              class Container>
class MyClass{
	Container<T> c;
};

// 这种模板命名必须声明在 funcation body 之外
template<typename T>
using Vec=vector<T, allocator<T>>;

template<typename T>
using Lst = list<T, allocator<T>>;

MyClass<MyString, Vec> c1;
MyClass<MyString, Lst> c2;

noexcept

就是在指定条件下保证不会抛出异常的意思

void foo() noexcept;
// 等于
void foo() noexcept(true);

// 保证在 x.swap(y) 不出错的条件下没有异常
void swap(Type& x, Type& y) noexcept(noexcept(x.swap(y))) {
    x.swap(y);
}

存于vector容器中的对象,必须在构造函数、move构造函数上声明noexcept。否则vector无法使用这种对象。在容器成长时需要将对象从旧数组赋值到新数组。

overried

struct Base {
    virtual void vfunc(float) {}
};
struct Derived1 : Base {
    virtual void vfunc(int) override {} // 此处没有正确override所以会报错
};
struct Derived2 : Base {
    virtual void vfunc(float) override {} // 此处正确override所以不会报错
};

final

类服务被继承,方法无法被重写

struct Base1 final{};

struct Base2 {
    virtual void f() final;
};

decltype

获得一个变量的类型表达式

map<string, float> coll;
decltype(coll)::value_type elem; // map<string, float>::value_type elem;

经常用于函数返回类型

template<typename T1, typename T2>
decltype(x+y) add(T1 x, T2 y);  // 此处decltype推测类型时无法得知x y的类型

template<typename T1, typename T2>
auto add(T1 x, T2 y) -> decltype(x+y); // 返回值类型是 T1 和 T2 两种类型相加的结果的类型

lambdas

完整形式
[...](...) mutable throwSpec -> retType {...}
[] 使用外部非静态变量
() 参数
auto fun = [] {
    cout << "hello!" << endl;
};
fun();

int id = 0;
auto f = [id] () mutable {//id的值在编译时复制到lambdas对象中,与外面的id是不同变量。没有mutable不能改变id
    cout << "id:" << id << endl;
    ++id;
};
id = 42;
f();  // id:0
f();  // id:1
cout << id; // 42

int id = 0;
auto f = [&id] () { // id 的引用在编译时复制到lambdas对象中,内部变化会引起外界的变化
    cout << "id:" << id << endl;
    ++id;
};
id = 42;
f();  // id:42
f();  // id:43
cout << id; // 44

Variadic Templates

利用参数个数和参数类型逐个递减的特性实现递归调用。

void fun() { // 这个空函数必须要有,因为递归到最后一定是调用空参数函数
	cout << endl;
}
template <typename T, typename... Types>
void fun(const T& firstArg, const Types&... args) {
    cout << firstArg << " ";
    fun(args...); // 递归调用模板函数
}

获得可变模板参数的个数使用

sizeof...(args);

递归调用

万能hash函数的例子,用于 unordered 容器

class CustomerHash {
public:
    // 自定义求hash的类函数
    size_t operator()(const Customer& c) const {
        return hash_val(c.xxx, c.yyy, c.zzz);//没有传入 seed,调用下方第一个函数
    }
};

/************    万能的哈希函数        ***********/
template<typename... Type>
inline size_t hash_val(const Types&... args) {
    size_t seed = 0;
    hash_val(seed, args...);
    return seed; // 返回值为 seed
}

template<typename T, typename... Type>//逐一取出 value,将n个变为 1 + (n-1) 个 variadic templates
inline size_t hash_val(size_t& seed, const Types&... args) {
    hash_combine(seed, val);
    hash_val(seed, args...);
}

template<typename T>//处理只有一个 value 的情况
inline void hash_val(size_t& seed, const T& val) {
    hash_combine(seed, val);
}

template<typename T>
inline void hash_combine(size_t& seed, const T& val) {
    seed ^= std::hash<T>()(val) + 0x9e377b9 + (seed<<6) + (seed>>2);
}class CustomerHash {
public:
    size_t op
};

递归继承 tuple 实现

在类中也有相似的用法

template<typename... Values> class tuple;
template<> class tuple<> {};

template<typename Head, typename... Tail> 
class tuple<Head, Tail> : private tuple<Tail...> {
    typedef tuple<Tail...> inherited;
protected:
    Head m_head; // 在初始化时赋值头元素
public:
    tuple() {}
    tuple(Head v, Tail... vtail) 
        : m_head(v), inherited(vtail...) {}//此处 inherited(vtail...) 是调用父类构造函数
    
    Head head() { return m_head; }
    inherited& tail() { return *this; } // 通过强转去除头元素
};

tuple<int, float, string>  -继承->  tuple<float, string>  -继承->  tuple<string>  -继承->  tuple<>

Rvalue references

用于解决不必要的复制。赋值时如果右边的值为Rvalue,左边的对象可以从右侧偷出资源而不是进行一次分配。

Lvalue:可以出现在 operator= 左侧,一般就是变量
Rvalue:只能出现在 operator= 右侧,临时对象

int a = 1; 
int b = 2;
a = b;      // ok
a = a + b;  // ok
// a b 都是左值
a + b = 3;  // ERROR a+b是右值,不能出现在左边

/*********************      临时对象是右值,但下面是例外      *********************/
string s1("hello");
string s2("world");
s1 + s2 = s2;       // ok 且 s1 s2 的值不变
string() = "world"; // ok 可以给临时对象赋值

complex<int> c1(3, 8), c2(1, 0);
c1 + c2 = complex<int>(4, 9);        // ok 且 c1 c2 的值不变
complex<int>() = complex<int>(4, 9); // ok
class MyClass {
    // copy assignment
    MyClass(const MyClass& str) {
        // 使用 malloc 分配新内存
        return *this;
    }
    // move assignment
    MyClass(const MyClass&& str) noexcept { // move ctor
        // 直接将指针指向同一个资源,旧对象接触对资源的引用
        return *this;
    }
};

vector<MyClass> vec();
vec.insert(MyClass()); // 此处向 vector 中插入一个临时对象,默认使用右值引用。

MyClass c1;
MyClass c2(std::move(c1)); // 使用 std::move 获得左值c1的右值引用,在这之后c1释放资源且**不能再被使用**

转交中的问题

void process(int& i) {}  // 1
void process(int&& i) {} // 2
void forward(int&& i) { process(i); } // 3

int a = 0;
process(a); // 调用 1
process(1); // 调用 2
process(std::move(a)); // std::move将左值变为右值,调用 2
forward(2); // 调用 3 再调用 1,因为在forward内部变量变为了一个 named object
forward(std::move(a)); // 调用 3 再调用 1
forward(a); // ERROR

const int& b = 1;
process(b); // ERROR no matching function fo call to 'process(cosnt int&)'
process(move(b)); // ERROR

int& x(5);

一个 move aware class。如果当它存于vector中,vector扩容时使用浅拷贝而非重新申请空间。

class MyString {
private:
    char* _data;
    size_t _len;
    // 复制内容
    void _init_data(const char* s) {
        _data = new char[_len+1];
        memcpy(_data, s, _len);
        _data[_len] = '\0';
    }
    
private:
    // default ctor
    MyString() : _data(NULL), _len(0) {}
    // ctor
    MyString(const char* p) : _len(strlen(p)) {
        _init_data(p);
    }
    // copy ctor
    MyString(const MyString& str) : _len(str._len) {
        _init_data(str._data); // COPY
    }
    // move ctor
    MyString(MyString&& str) noexpect
        : _data(str._data), _len(str._len) {
            str._len = 0;
            str._data = NULL; // 重要 原对象解除对资源的引用
    }
    // copy assignment
    MyString& operator=(const string& str) {
        if (this != &str) {
            _len = str._len;
            _init_data(str._data);
        } else {}
        return *this;
    }
    // move assignment
    MyString& operator=(MyString&& str) noexcept {
        if (this != &str) {
            if (_data)
                delete _data;
            _len = str._len;
            _data = str._data;
            str._len = 0;
            str._data = NULL; // 重要 原对象解除对资源的引用
        }
        return *this;
    }
    // dtor
    virtual ~MyString() {
        if (_data) {
            delete _data;
        }
    }
    
    bool operator<(const MyString& rhs) const {
        return string(this->data) < string(rhs._data);
    }
    bool operator==(const MyString& rhs) const {
        return string(this->data) == string(rhs._data);
    }
    char* get() const { return _data; }
};
posted @ 2022-06-16 09:27  某某人8265  阅读(28)  评论(0编辑  收藏  举报