Ver Fonte

一些C++11的变化

usuiforhe há 4 anos atrás
pai
commit
a6a74050c3
1 ficheiros alterados com 499 adições e 0 exclusões
  1. 499 0
      cpp/C++11&14.md

+ 499 - 0
cpp/C++11&14.md

@@ -0,0 +1,499 @@
+<!--
+ * @Author: your name
+ * @Date: 2021-10-21 10:56:48
+ * @LastEditTime: 2021-11-18 21:08:59
+ * @LastEditors: Please set LastEditors
+ * @FilePath: \MarkdownLog\cpp\C++11&14.md
+-->
+
+- C++的一些版本
+  - C++98(1.0)
+  - C++03
+  - C++11(2.0)
+  - C++14
+
+## Variadic Templates 可变参数模板
+
+```cpp
+void printX()
+{}
+
+template <typename T, typename... Types>
+void printX(const T& firstArg, const Types&... args)
+{
+    cout << firstArg << endl;
+    printX(args...);
+}
+```
+
+> 通过`sizeof...(args)`可以知道args中包括多少个参数
+
+通过可变参数模板,可以帮助我们使用递归做一些操作  
+
+```cpp
+// 可变参数模板的用例
+
+#include <functional>
+template<typename T>
+inline void hash_combine(size_t& seed. const T& val)// 函数_4
+{
+    seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed<<6) + (seed >> 2);
+}
+
+template<typename T>
+inline void hash_val(size_t& seed, const T& val){// 函数_3
+    hash_combine(seed, val);
+}
+
+template<typename T, typename... Types>
+inline void hash_val(size_t& seed, const T& val, const Types&... args)// 函数_2
+{
+    hash_combine(seed, val);
+    // 根据args...的参数个数来执行 函数_2 或者 函数_3
+    hash_val(seed, args... );
+}
+
+template<typename... Types>
+inline size_t hash_val(const Types&... args)// 函数_1
+{
+    size_t seed = 0;
+    // 虽然 函数_1 和 函数_2 都符合执行条件,但是 函数_2 相比而言更特化,所以执行 函数_2
+    hash_val(seed, args);
+    return seed;
+}
+
+class CustomerHash{
+    public:
+    std::size_t operator()(const Customer& c) const{
+        // 因为第一个fname不是size_t类型,所以执行的是 函数_1 hash_val
+        return hash_val(c.fname, c.lname, c.no);    
+    }
+}
+```
+
+> 这里用args配合递归不停的切分参数
+
+```cpp
+template<typename... Values> class tuple;
+template<> class tuple<> {}; // 作为中止点
+
+template<typename Head, typename... Tail>
+class tuple<Head, Tail...> : private tuple<Tail>
+{
+    typedef tuple<Tail...> inherited;
+public:
+    tuple() {}
+    tuple(Head v, Tail... vtail) : m_head(v), inherited(vtail) {}
+    typename Head::type head() {return m_head;}
+    inherited& tail() {return *this;}
+
+protected:
+    Head m_head;
+}
+
+// tuple<int, float, string> t(41, 6.3, "Niko");
+// t.head() -> 41
+// t.tail() -> tuple<float, string>(6.3, "Niko")
+// t.tail()->head() -> 6.3
+
+```
+
+> 这里最上面有个空的 tuple<>,是作为递归继承的中止点  
+> 这里用递归继承的方式做出了tuple  
+
+## 模板中的空格
+
+```cpp
+vector<list<int> > // 全版本支持
+vector<list<int>> // C++11之后没有空格也行
+```
+
+> 因为老版本一些编译器会将`>>`识别为操作符,所以需要加空格  
+> C++11之后不加空格也行  
+
+## nullptr ans std::nullptr_t
+
+`nullptr` 指的是 空指针,C++11允许用`nullptr`代替0或者`NULL`
+
+```cpp
+void f(int);
+void f(void*);
+f(0);               // 运行  f(int)
+f(NULL);            // 运行  f(int),如果是其他数,就不清楚具体调用哪个了
+f(nullptr);         // 云心  f(void*)
+```
+
+```cpp
+#if defined(__cplusplus) && __cplusplus >= 201103L
+#ifndef __GXX_NULLPTR_T
+#define __GXX_NULLPTR_T
+    typedef decltype(nullptr) nullptr_t;
+```
+
+## auto
+
+```cpp
+auto i = 20;    // i 的 类型是 int
+
+double f();
+auto d = f();   // d 的 类型是 double
+
+vector<string> v;
+auto pos = v.begin();   // pos 的 类型是 vector<string>::iterator
+
+auto l = [](int x) -> bool {
+    // ...
+};              // l 的类型是 lambda表达式
+
+```
+
+> 使用`auto`主要是用在数据类型 **太长** 或者 **太复杂** 的情况 (比如迭代器、lambda的类型)  
+> 倒也不是什么情况都用`auto`
+
+```cpp
+// 标准库中对auto的使用
+#if __cplusplus >= 201103L
+    inline auto operator- (const reverse_iterator<_IteratorL> &__x, 
+        const reverse_iterator<_IteratorR> &__y)
+        -> decltype(__y.base() - __x.base())
+#else
+    inline typename reverse_iterator<_IteratorL>::difference_type 
+        operator- (const reverse_iterator<_IteratorL>& __x, 
+        const reverse_iteratror<_IteratorR>& __y)
+#endif
+```
+
+## unifor initialization 一致性初始化
+
+初始化可能发生在小括号、中括号、赋值中
+
+```cpp
+Rect r1 = {3, 4, 5};        // 大括号
+Rect r1(3, 4, 5);           // 小括号
+int values[5] = {1, 3, 4, 5};   // 赋值
+```
+
+C++11引入**一致性初始化**——大括号(虽然用以前的也没错)
+
+```cpp
+int values[]{1, 3, 4, 5}; 
+vector<int> v{1, 3 ,4 ,5};
+complex<duoble> c{3.0, 4.0};
+```
+
+> 变量之后直接加上大括号即表示初始化  
+
+当编译器看到`{t1, t2, t3...}`便做出一个`initializer_list<T>`,他关联至一个`array<T, n>`,调用函数(比如构造函数)时`arry`中的元素被逐个分解传给函数;如果调用函数的参数就是`initializer_list<T>`,就不会注意分解而是直接整个传给函数  
+
+比如:`vector<int> v{1, 3 ,4 ,5};` 中的初始化参数 `{1, 3 ,4 ,5}`转换成 `initlializer_list<int>`,而这个关联`array<int, 4>`容器
+
+## initalizer_list 初始化列表
+
+```cpp
+int i;          // 没有被初始化
+int j{};        // j被初始化为0
+int *p;         // p没有被初始化
+int *q{};       // q被初始化为nullptr
+```
+
+```cpp  
+int x1(4.3);            // ok
+int x2 = 4.3;           // ok
+int x3 {4.3};           // 部分平台Error,部分平台warning
+int x4 = {5.3};         // 部分平台Error,部分平台warning
+
+char c1{7};             // ok
+char c2{9999};          // 部分平台Error,部分平台warning
+
+std::vector<int> v1{1, 3, 4, 5};            // ok
+std::vector<int> v2{1.0, 3.0, 4.0, 5.0};    // 部分平台Error,部分平台warning
+```
+
+> 初始化列表的作用,还可以帮助检查一些错误
+
+```cpp
+
+void print(std::initlializer_list<int> vals)
+{
+    for(auto p = vals.begin(), p != vals.end(); ++p){
+        std::cout << *p << std::endl;
+    }
+}
+
+print({1, 3, 4, 5});
+```
+
+> 证明`{t1, t2, t3...}`被转换成`initializer_list<T>`  
+
+```cpp
+class P{
+public:
+    P(int a, int b){
+        std::cout << "(int ,int)" << std::endl;
+    }
+    P(initializer_list<int> initlist){
+        std::cout << "initializer_list" << std::endl;
+    }
+};
+
+P p(7, 7);          //  输出 (int ,int)
+P p{7, 7};          //  输出 initializer_list
+P r{7, 5, 4};       //  输出 initializer_list
+P p = {7, 7};       //  输出 initializer_list
+```
+
+> 如果不存在第二个`initializer_list`的构造函数,则第三个对象 r 将创建失败
+
+## explicit
+
+```cpp
+
+/**
+ * non-explicit-one-argument 只要一个实参就够了的构造函数
+ * 下面例子中:没有写oprator * (Fraction, int)的操作符重载也不会报错,因为编译器将4通过一个参数的构造变成Fraction(4, 1)类型
+ * 当构造函数前加上explicit关键字时,编译器不会进行隐式构造函数将4变成Fraction(4, 1)
+ * 
+ * ——————————————————————————————————————————————————————————————————————————————
+ * 如果构造函数不存在explicit,且有operator double()转换函数,编译器报错,因为存在二义性即两种方式都可以行得通且没有优先级这么一说
+ * 如果构造函数存在explicit,且有operator double()转换函数,编译器不报错,因为只有一条路可走,将Fraction转换成double计算
+ * 如果构造函数不存在explicit,且没有operator double()转换函数,编译器不报错,因为只有一条路可走,将4转换成Fraction计算
+ **/
+
+class Fraction
+{
+  public:
+    // 这里构造函数只需要一个参数其实也够
+    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
+    //explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
+
+    Fraction operator*(const Fraction &f)
+    {
+        return Fraction(f.m_numerator * this->m_numerator, f.m_denominator * this->m_denominator);
+    }
+    std::string getString()
+    {
+        char numerator[10];
+        char denominator[10];
+        memset(numerator, 0, sizeof(numerator));
+        memset(denominator, 0, sizeof(denominator));
+        itoa(m_numerator, numerator, 10);
+        itoa(m_denominator, denominator, 10);
+        return std::string(std::string(numerator) + std::string("/") + std::string(denominator));
+    }
+
+  private:
+    int m_denominator; // 分母
+    int m_numerator;   // 分子
+};
+
+int main()
+{
+    Fraction f(3, 5);
+    Fraction f2 = f * 4;
+    std::cout << f2.getString() << std::endl;
+    system("pause");
+    return 0;
+}
+```
+
+> explicit告诉编译器,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换
+
+## range-based for statement
+
+```cpp
+// for(decl : coll){
+//     statement
+// }
+
+for(int i : {2, 3, 4, 5, 6}){
+    cout << i << endl;
+}
+
+vector<double> vec;
+//...
+for(auto elem : vec){
+    cout << elem << endl;
+}
+for(auto &elem : vec){
+    elem *= 3;
+}
+
+// range-based for 的底层实现类似
+
+for(auto _pos = coll.begin(), _end = coll.end(); _pos != _end; ++pos){
+    decl = *_pos;
+    // 对值的操作
+}
+// 或者
+for(auto _pos = begin(coll), _end = end(coll); _pos != _end; ++pos){
+    decl = *_pos;
+    // 对值的操作
+}
+```
+
+**下面的写法会报错**
+
+```cpp
+class C{
+public:
+    explicit C(const string& s); // 禁止string隐式转换成C
+    // ...
+}
+
+vector<string> vs;
+for(const C& elem : vs){
+    cout << elem << endl;   // 报错,因为字符串不能隐式的转换成C
+}
+```
+
+## =default, =delete
+
+如果自行定义一个构造函数,那么编译器不会再给一个默认构造函数
+
+如果强制加上 `=default` 就可以重新获得并使用默认构造函数
+
+```cpp
+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;
+};
+```
+
+
+```cpp
+class Foo{
+public:
+    Foo(int i) : _i(i) {}
+    Foo() = default;    // 构造函数可以有多个,这里希望编译器提供默认构造
+
+    Foo(const Foo& x) : _i(x._i) {}
+    // Foo(const Foo&) = default;   // Error 拷贝构造函数不可被重载
+    // Foo(const Foo&) = delete;    // Error 拷贝构造函数不可被重载
+
+    Foo& operator=(const Foo& x) {_i = x._i; return *this;}
+    // Foo& operator=(const Foo& x) = default; // Error 赋值不可被重载
+    // Foo& operator=(const Foo& x) = delete;  // Error 赋值不可被重载
+
+    void func1() = default; // Error 不可作为default
+    void func2() = delete;  // ok 不过如果不需要可以不写这个函数…
+
+    ~Foo() = default;
+    // ~Foo() = delete // 可以new不可delete
+};
+```
+
+- `=default` 除了默认成员函数(构造、析构、拷贝、赋值等)之外,其他函数使用编译报错(很明显,其他函数也没有所谓的默认)
+- `=delete`可以用在任何函数上(`=0`只能用于`virtual`函数)
+
+> 一般而言,如果成员对象中存在指针,建议考虑深拷贝、浅拷贝的问题,需要自定义拷贝、赋值函数
+
+## Alias Templat (template typedef)化名模板
+
+```cpp
+template<typename T>
+using Vec = std::vector<T, MyAlloc<T>>;
+
+Vec<int> coll; // = std::vector<int, MyAlloc<int> coll;
+```
+
+## decltype
+
+使用该关键字可以找到表达式的参数类型
+
+```cpp
+map<string, float> coll;
+decltype(coll)::value_type elem;    // C++11可以通过对象使用
+map<string,float>::value_type elem; // C++11之前必须指明类型使用
+
+template<typename T1, typename T2>
+auto add(T1 x, T2 y) -> decltype(x+y);  // 通过decltype动态设置返回参数类型
+
+decltype(x+y) add(T1 x, T2 y);      // 这样写报错,因为decltype先不知道x、y是什么,在后面的函数参数中才知道
+
+auto cmp = [](const Person& p1, const Person& p2){
+    // 比较大小
+};
+std::set<Person, decltype(cmp)> coll(cmp);
+```
+
+## lambda表达式
+
+```cpp
+[]{
+    std::cout << "hello world" << std::endl;
+}(); // 输出 hello world
+
+auto l = []{
+    std::cout << "hello world" << std::endl;
+};
+l();// 输出 hello world
+
+[导入器](参数) mutable 异常 -> 返回类型 {
+    函数体
+}
+
+```
+
+> 导入器,取用的外部变量(传值、传引用)  
+> 参数,正常函数参数  
+> mutable 是否可以修改值  
+> 异常 一些特殊操作抛出异常  
+
+```cpp
+{
+    int id = 0;
+    // 值传递
+    auto f = [id]() mutable {
+        std::cout << "id : " << id << std::endl;
+        ++id;// 如果没有mutable,则id不可执行++操作
+    };
+
+    id = 42;
+    f();                            // 输出 id : 0
+    f();                            // 输出 id : 1
+    f();                            // 输出 id : 2
+    std::cout << id << std::endl;   // 输出 id : 42
+}
+
+{
+    int id = 0;
+    // 引用传递
+    auto f = [&id](int param)  {
+        std::cout << "id : " << id << std::endl;
+        ++id; ++param;
+    };
+
+    id = 42;
+    f(7);                            // 输出 id : 42
+    f(7);                            // 输出 id : 43
+    f(7);                            // 输出 id : 44
+    std::cout << id << std::endl;   // 输出 id : 45
+}
+
+{
+    int id = 0;
+    auto f = [id]() {
+        std::cout << "id : " << id << std::endl;
+        ++id;
+    };
+    // 报错 没有mutable 不可更改
+    id = 42;
+    f();                           
+    f();                           
+    f();                           
+    std::cout << id << std::endl;  
+}
+
+// 一个 = 表示 其他所有对象都是值传递, id为引用传递
+auto f = [=, &id]{ 
+    // ...
+};
+```