第8課 列表初始化(3)_防止類型收窄、explicit關鍵字


1. 防止類型收窄

(1)類型收窄:指的是導致數據內容發生變化或者精度丟失隱式類型轉換

(2)類型收窄的幾種情況

  ①從浮點數隱式轉換為整型數,如int i=2.2;

  ②從高精度浮點數隱式轉換為低精度浮點數。如從long double轉double或float。

  ③從整型數隱式轉換為浮點數,並且超出浮點數的表示范圍,如float=(unsigned long Long)-1,注意這表示將-1先強制轉換為unsigned long long,再隱式轉換為float。

  ④從整型數隱式轉換為長度較短的整型數,並且超出其表示范圍。如char x=65536。

(3)在C++98/03中,類型收窄編譯器不會報錯,而C++11中可以通過列表初始化來檢查及防止類型收窄

【編程實驗】防止類型收窄

#include <iostream>
#include <vector>
using namespace  std;

int main()
{
    int x1(5.3);   //ok,x1=5
    int x2 = 5.3;  //ok,x2=5
    int x3 {5.3};  //error:narrowing,gcc給的是warning
    int x4 = {5.3};//同上
    char c1{7};    //ok
    char c2{99999};//error:narrowing,gcc給的是warning
    std::vector<int> v1{1,2,3,4,5}; //ok
    std::vector<int> v2{1,2,3,4,5.6}; //error:narrowing,gcc給的是warning
    
    float fa = 1e40;  //ok,double->float
    float fb = {1e40};//error,double->float,超過float能表示的范圍
    float fc = (unsigned long long)-1; //將-1(0xFFFFFFFF)強轉,再隱式轉成float
    float fd = {(unsigned long long)-1}; //error
    float fe = (unsigned long long)1; //將1強轉,再隱式轉成float
    float ff = {(unsigned long long)1}; //ok
    
    const int x = 1024, y = 1; //注意x,y被const修飾
    char c = x;  //ok
    char d = {x};//error
    char e = y;  //ok
    char f = {y};//ok。如果y為int型,則該行會出錯。但由於加了const並且值為1。編譯器
                 //認為這樣的轉換是安全的。
    
    
    return 0;
}
/*
*******************gcc上的測試結果*********************
e:\Study\C++11\8>g++ -std=c++11 test1.cpp
test1.cpp: In function 'int main()':
test1.cpp:9:13: error: narrowing conversion of '5.2999999999999998e+0' from 'double' to 'int' inside { } [-Wnarrowing]
test1.cpp:10:15: error: narrowing conversion of '5.2999999999999998e+0' from 'double' to 'int' inside { } [-Wnarrowing]
test1.cpp:12:15: error: narrowing conversion of '99999' from 'int' to 'char' inside { } [-Wnarrowing]
test1.cpp:14:33: error: narrowing conversion of '5.5999999999999996e+0' from 'double' to 'int' inside { } [-Wnarrowing]
test1.cpp:17:18: error: narrowing conversion of '1.0e+40' from 'double' to 'float' inside { } [-Wnarrowing]
test1.cpp:19:36: error: narrowing conversion of '18446744073709551615ull' from 'long long unsigned int' to 'float' inside { } [-Wnarrowing]
test1.cpp:24:11: warning: overflow in implicit constant conversion [-Woverflow]
test1.cpp:25:13: error: narrowing conversion of '1024' from 'int' to 'char' inside { } [-Wnarrowing]
  
*******************vc2015上的測試結果*********************
test1.cpp(9): error C2397: 從“double”轉換到“int”需要收縮轉換
test1.cpp(10): error C2397: 從“double”轉換到“int”需要收縮轉換
test1.cpp(12): error C2397: 從“int”轉換到“char”需要收縮轉換
test1.cpp(14): error C2398: 元素“5”: 從“double”轉換到“int”需要收縮轉換
test1.cpp(17): error C2397: 從“double”轉換到“float”需要收縮轉換
test1.cpp(19): error C2397: 從“unsigned __int64”轉換到“float”需要收縮轉換
test1.cpp(25): error C2397: 從“const int”轉換到“char”需要收縮轉換
*/

2. explicit關鍵字

(1)explicit用於阻止編譯器的隱式轉換,一般用於修飾構造函數

(2)C++98/03由於不能使用{}列表初始化,即隱式轉換只發生在調用帶一個參數的構造函數中。但C++11允許用{}列表初始化對象,這可能會隱式調用帶多個參數的構造函數

(3)C++11中,explicit可用於修飾帶多個參數的構造函數以防止隱式轉換。(注意C++98/03中explicit被用於修飾只帶一個參數的構造函數,如果修飾帶多個參數的構造函數則無效)

【編程實驗】explicit修飾帶多參的構造函數

  1 #include <iostream>
  2 using namespace std;
  3 
  4 class Test
  5 {
  6 public:
  7     Test(int a, int b)
  8     {
  9         cout <<"Test(int a, int b)" << endl;
 10     }
 11     
 12     Test(initializer_list<int>)
 13     {
 14         cout <<"Test(initializer_list<int>)" << endl;
 15     }
 16     
 17     explicit Test(int a, int b, int c)
 18     {
 19         cout <<"explicit Test(int a, int b, int c)" << endl;
 20     }
 21 };
 22 
 23 void func(const Test& t)
 24 {
 25     
 26 }
 27 
 28 struct Complex
 29 {
 30     int real, imag;
 31     
 32     //explicit 
 33     Complex(int re, int im = 0):real(re), imag(im){}
 34     
 35     Complex operator+(const Complex& x)
 36     {
 37         return Complex((real+x.real),(imag+x.imag));
 38     }
 39 };
 40 
 41 int main()
 42 {
 43     Complex c1(12, 5);
 44     Complex c2 = c1 + 5;//會試圖將5轉換為Complex類型,所以會隱式調用構造
 45                         //函數。為了阻止這種行為,可以在構造函數前加explicit
 46     
 47     //1.由於Test帶有一個initializer_list參數的構造函數,因此,下列
 48     //用{}初始化的對象都會直接調用該構造函數。
 49     //2.如果注釋掉上述構造函數,則編譯器會將{...}分解並傳給相應的
 50     //構造函數,如果找不到相應的帶多參的構造函數,則直接報錯。
 51     Test t1(77, 5);       //Test(int a, int b)
 52     Test t2{77, 5};       //Test(initializer_list<int>)
 53     Test t3{77, 5, 42};   //Test(initializer_list<int>)
 54     Test t4 = {77, 5};    //Test(initializer_list<int>)
 55     Test t5 = {77, 5, 42};//Test(initializer_list<int>)
 56     Test t6(77, 5, 42);   //ok,顯式調用explicit Test(int a, int b, int c)
 57     
 58     func({47, 11});           //Test(initializer_list<int>)
 59     func({47, 11, 3});        //Test(initializer_list<int>)
 60     func(Test{47, 11});       //Test(initializer_list<int>)
 61     func(Test{47, 11, 3});    //Test(initializer_list<int>)
 62     
 63     Test t11{77, 5, 42, 500};   //Test(initializer_list<int>)
 64     Test t12 = {77, 5, 42, 500};//Test(initializer_list<int>)
 65     Test t13 {10};  //Test(initializer_list<int>)
 66     
 67     return 0;
 68 }
 69 /*測試結果:
 70 **************************不注釋Test中帶有一個initializer_list參數的構造函數***************************
 71 e:\Study\C++11\8>g++ -std=c++11 test2.cpp
 72 e:\Study\C++11\8>a.exe
 73 Test(int a, int b)
 74 Test(initializer_list<int>)
 75 Test(initializer_list<int>)
 76 Test(initializer_list<int>)
 77 Test(initializer_list<int>)
 78 explicit Test(int a, int b, int c)
 79 Test(initializer_list<int>)
 80 Test(initializer_list<int>)
 81 Test(initializer_list<int>)
 82 Test(initializer_list<int>)
 83 Test(initializer_list<int>)
 84 Test(initializer_list<int>)
 85 Test(initializer_list<int>)
 86 
 87 **************************不注釋Test中帶有一個initializer_list參數的構造函數***************************
 88 e:\Study\C++11\8>g++ -std=c++11 test2.cpp -fno-elide-constructors
 89 test2.cpp: In function 'int main()':
 90 test2.cpp:55:22: error: converting to 'Test' from initializer list would use explicit constructor 'Test::Test(int, int, int)'
 91   Test t5 = {77, 5, 42};
 92 
 93 test2.cpp:59:18: error: converting to 'const Test' from initializer list would use explicit constructor 'Test::Test(int, int, int)'
 94   func({47, 11, 3});  
 95 
 96 test2.cpp:63:25: error: no matching function for call to 'Test::Test(<brace-enclosed initializer list>)'
 97   Test t11{77, 5, 42, 500}; 
 98 
 99 test2.cpp:64:28: error: could not convert '{77, 5, 42, 500}' from '<brace-enclosed initializer list>' to 'Test'
100   Test t12 = {77, 5, 42, 500};
101 
102 test2.cpp:65:14: error: no matching function for call to 'Test::Test(<brace-enclosed initializer list>)'
103   Test t13 {10};
104 */


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM