{} 初始化補充

  • 主要內容

    • {}好處

      • 可以在幾乎所有的場景使用的初始化語法.

      • 規避語法: int a(),實際是成員函數聲明,編譯器優先這樣解析的.

      • 可以寫c++98不支持的語法.

    • 內容

      • 優先匹配: initialize,成員函數,成員變量.

  • 統一初始化

    • 歷史

      • 以前的初始化方式千奇百怪.
      • C++11之後編譯器創建了{},可以支持各種環境下的初始化.

    • 案例

      int main() {
         int a = 0;
         int b(1);
         int c{1};
         int d = {1};
      }
      
    • 分析

      • c,d的方式一樣,後面就介紹c.

  • 賦值和構造

    • 案例

      #include <iostream>
      
      class D {
      public:
         D() {std::cout << __LINE__ << std::endl;}
         D(const D& d) {std::cout << __LINE__ << std::endl;}
         void operator=(const D& d){std::cout << __LINE__ << std::endl;}
      };
      
      int main() {
         D a;
         D b = a;
         b = a;
         return 0;
      }
      
    • 分析

      • D b = a是用拷貝構造調用,後面的則是賦值構造.

      • 新手一臉懵逼.

  • 容器默認值

    • 案例

      #include <vector>
      int main() {
         std::vector<int> v {1,2,3,4,5};
         return 0;
      }
      
      • gcc test.cpp -std=c++98編譯不過

      • C++11支持了std::initializer_list類型,語法上也進行了支持.

    • 統一初始化和{}初始化.

      • 統一初始化時俗語.方法.

      • {}胡括號初始化是C++的語法.

  • 成員函數默認值

    • 案例

      class T{
      private:
         int a{0};
         int b = 0;
         int c(0);
      };
      int main(){
      
      }
      
    • 分析

      • c++在語法上不支持.

  • 有拷貝構造和無拷貝構造

    • 案例

      class T{
      public:
         T(int){}
         T(const T&) = delete;
      };
      int main(){
         T a {0};
         T b (0);
         T c = 0;
      }
      
    • 分析

      • 無拷貝構造不允許c方式的構造.有才允許,語法上的問題.

  • 窄化

    • 案例

      int main(){
         int a(1.0 + 2);
         int b{1 + 2};
         int c{1.0 + 2};
      }
      
    • 分析

      • 窄化: 浮點和非浮點之間轉換,賦值溢出.

      • ()默認不管,{}編譯不通過,語法支持,如果()也支持會有很大的問題.

      • 根據ocp原則,增可以,改動不可以.兼容性會很差.

  • 聲明和定義

    • 案例

      int main(){
         int c();
         int b = c;
      }
      
    • 分析

      • c是函數類型,這是編譯器支持,將具有二義性的看成聲明.

      • 這裏就是函數的聲明.使用{}可以規避,因爲語法上支持這種,沒有二義性.

  • 小結

    • 前面介紹了優點.下面介紹一下缺點.

    • 主要從: {}初始化,std::initializer_lists,和構造函數重載.

    • 行爲看起來像是在幹一件事兒,其實各幹各的.

    • 構造無std::initializer_lists,(){}乾的一樣的事情.

  • auto

    • 案例

      #include <initializer_list>
      template <typename T>
      void show(T&& d) {
         d.error();
      }
      
      int main() {
         auto s = {1,2,3,4};
         show(s);
      }
      
    • 分析

      • 默認推導出來的是std::initializer_list<int>.

      • 兩者之間有衝突.

  • std::initializer_lists(){}

    • 說明

      • 兩者是一個意思.
  • std::initializer_lists

    • 案例

      #include <iostream>
      #include <initializer_list>
      class T {
      public:
         T(long,double){std::cout<<__LINE__<<std::endl;}
         T(std::initializer_list<bool>){std::cout<<__LINE__<<std::endl;}
      };
      int main() {
         T s{1,2.0};
      }
      
    • 分析

      • 能匹配initializer_list就一定匹配轉換.即使進行多個步驟.

  • 窄化

    • 非窄化

      #include <iostream>
      #include <initializer_list>
      class T {
      public:
         T(long,double){std::cout<<__LINE__<< ":dd" <<std::endl;}
         T(std::initializer_list<bool>){std::cout<<__LINE__ << ":ss" <<std::endl;}
      };
      int main() {
         T s{1.0,2.0};
         T s{1,2.0};
      }
      
    • 分析

      • 浮點轉bool編譯器不會報錯,浮點轉非浮點會.

  • 無法轉化std::initializer_list

    • 案例

      #include <iostream>
      #include <string>
      #include <initializer_list>
      class T {
      public:
         T(long,double){std::cout<<__LINE__<< ":dd" <<std::endl;}
         T(std::initializer_list<std::string>){std::cout<<__LINE__ << ":ss" <<std::endl;}
      };
      int main() {
         T a{1,2.0};
         T b{1.0,2.0};
      }
      
    • 分析

      • 無法轉化爲std::string類型,所以恢復構造並檢驗是否窄化.

  • 默認構造還是std::initializer_list

    • 案例

      #include <iostream>
      #include <string>
      #include <initializer_list>
      class T {
      public:
         T(){std::cout<<__LINE__<< ":dd" <<std::endl;}
         T(long,double){std::cout<<__LINE__<< ":dd" <<std::endl;}
         T(std::initializer_list<std::string>){std::cout<<__LINE__ << ":ss" <<std::endl;}
      };
      int main() {
         T a{};
         T b({});
         T c{{}};
         return 0;
      }
      
    • 分析

      • {}無參,表示默認構造.而不是無參的std::initializer_list.

      • b,c表示無參std::initializer_list.

  • 模板不支持

    • 案例

      #include <iostream>
      template <typename T>
      void show( T&& a) {
      
      }
      
      int main () {
         show({1,2,3,4,5});
      }
      
    • 分析

      • 需要兩次推導,不支持.分別是template推導和{}裏面的類型推導.

  • 總結

    • 注意

      • 如果構造有重載std::initializer_list,會對創建產生影響,需要謹慎選擇{},().

    • 優先級

      • std::initializer_list 盡力推導,可以通過窄化推導的則報錯.不往下繼續.

      • 無法推導則拆分爲構造函數.

      • 無法推導則拆分爲成員變量初始化.

      • 成員變量按照元素挨個初始化.

  • 發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章