初始化、賦值、默認初始化、列表初始化、類內初始值、直接初始化與拷貝初始化

文章目錄

  • 初始化和賦值的區別
  • 什么是默認初始化?
  • 列表初始化
    • 列表初始化的使用場景
    • 不適合使用列表初始化的場景
  • 類內初始值
  • 混用string對象和C風格字符串
  • 數組與vector對象
    • 關于vector對象
    • 兩者間的初始化關系
  • 直接初始化與拷貝初始化


初始化和賦值的區別

  • 初始化的含義是創建變量時賦予其一個初始值
  • 賦值的含義時把對象的當前值擦除,而已一個新值來替代。


什么是默認初始化?

如果定義變量時沒有指定初值,則變量被默認初始化,此時變量被賦予了 “默認值”。默認值到底是什么由變量類型決定,同時定義變量的位置也會對此有影響。

內置類型的默認值由定義的位置決定, 定義于任何函數體之外的變量被初始化為0;定義于函數體內部的內置類型將不被初始化一個未被初始化的內置類型變量的值是未定義的,試圖拷貝或以其他形式訪問此類值將引發錯誤。



列表初始化

C++定義了初始化的好幾種不同形式,通常定義一個變量并初始化的方式有以下四種:

int x = 0;
int x = {0};
int x{0};
int x(0);

使用花括號來初始化變量在C++11新標準中得到了全面應用。這種初始化的形式被程為列表初始化(list initialization)。現在,無論是初始化對象,還是某些時候為對象賦新值,都可以使用列表初始化。

列表初始化的使用場景

  1. 列表初始化可被用于以下場景:
// Vector 接收了一個初始化列表。
vector<string> v{"foo", "bar"};// 不考慮細節上的微妙差別,大致上相同。
vector<string> v = {"foo", "bar"};// 可以配合 new 一起用。
auto p = new vector<string>{"foo", "bar"};// map 接收了一些 pair, 列表初始化大顯神威。
map<int, string> m = {{1, "one"}, {2, "2"}};// 初始化列表也可以用在返回類型上的隱式轉換。
vector<int> test_function() { return {1, 2, 3}; }// 初始化列表可迭代。
for (int i : {-1, -2, -3}) {}// 在函數調用里用列表初始化。
void TestFunction2(vector<int> v) {}
TestFunction2({1, 2, 3});
  1. 用戶自定義類型也可以定義接收 std::initializer_list<T> 的構造函數和賦值運算符,以自動列表初始化:
class MyType {public:// std::initializer_list 專門接收 init 列表。MyType(std::initializer_list<int> init_list) {for (int i : init_list) append(i);}MyType& operator=(std::initializer_list<int> init_list) {clear();for (int i : init_list) append(i);}
};
MyType m{2, 3, 5, 7};
  1. 最后,列表初始化也適用于常規數據類型的構造,哪怕沒有接收 std::initializer_list<T> 的構造函數。
// MyOtherType 沒有 std::initializer_list 構造函數,
// 直接上接收常規類型的構造函數。
class MyOtherType {public:explicit MyOtherType(string);MyOtherType(int, string);
};
MyOtherType m = {1, "b"};
// 不過如果構造函數是顯式的(explict),就不能用 `= {}` 了。
MyOtherType m{"b"};

不適合使用列表初始化的場景

值得注意的是,當用于內置類型的變量時,如果使用列表初始化且初始值存在丟失信息的風險,則編譯器將報錯:

long double ld = 3.1415926536;
int a{ld}, b = {ld}; // 錯誤:轉換未執行,因為存在丟失信息的危險
int c(ld), d = ld; // 正確:轉換執行,且確實丟失了部分值

使用 long double 的值初始化 int 變量時可能丟失數據,所以編譯器拒絕了 ab 的初始化請求。其中,至少 ld 的小數部分會丟失掉,而且某些情況下 int 也可能存不下 ld 的整數部分。

同時,千萬別直接列表初始化 auto 變量,因為可讀性不高:

auto d = {1.23};        // d 類型是 std::initializer_list<double>
auto d = double{1.23};  // d 類型為 double, 并非 std::initializer_list.


類內初始值

C++11標準規定,可以為數據成員提供一個類內初始值(in-class initializer)。創建對象時,類內初始值將用于初始化數據成員。沒有初始值的成員將被默認初始化。

對類內初始值的限制如下:

  1. 放在花括號里
  2. 放在等號右邊
  3. 不能使用圓括號

因為我們無法避免這樣的情況,有時函數聲明也會用到圓括號:

class Widget 
{
private: typedef int x;int z(x);
};

因此用圓括號為類內成員提供類內初始值容易產生二義性,編譯器會覺得該語句語義不明。



混用string對象和C風格字符串

我們都知道允許使用字符串字面值來初始化string對象:

string s("Hello World!");

C++規定,任何出現字符串字面值的地方都可以用以空字符結束的字符數組來替代

  • 允許使用以空字符結束的字符數組來初始化string對象或為string對象賦值。
  • 在string對象的加法運算中允許使用以空字符結束的字符數組作為其中一個運算對象(不能兩個對象都是);在string對象的復合賦值運算中允許是用以空字符結束的字符數組作為右側的運算對象

上述性質反過來并不成立:如果程序的某處需要一個C風格字符串,無法直接用string對象來替代它。

例如:不能使用string對象直接初始化指向字符的指針。為了實現這一功能,string專門提供了一個名為c_str的成員函數:

char *str = s; // 錯誤:不能用string對象初始化char*
const char *str = s.c_str; // 正確

函數返回結果使用一個指針,該指針指向一個以空字符結束的字符數組,而這個數組所存的數據恰好與哪個string對象的一樣。結果指針的類型是const char*,從而確保我們不會改變字符數組的內容。

PS:由于我們無法保證c_str函數返回的數組一直有效,如果后續的操作改變了s的值就可能讓之前返回的數組失去效用。因此,如果執行完c_str()函數后程序想一直都能使用其返回的數組,最好將該數組重新拷貝一份。



數組與vector對象

關于vector對象

vector是模板而非類型,由vector生成的類型必須包含vector中元素的類型,如:

vector<int>

兩者間的初始化關系

  1. 不允許使用一個數組為另一個內置類型的數組賦初值
  2. 不允許使用vector對象初始化數組
  3. 允許使用數組來初始化vector對象

實現第三點只需要指明要拷貝區域的首元素地址和尾后地址就可以了:

int int_arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ivec(begin(int_arr), end(int_arr));

用于創建ivec 的兩個指針實際上指明了用來初始化的值在數組int_arr中的位置,分別用標準庫函數begin和end來計算int_arr的首指針和尾后指針。在最終結果中,ivec將包含6個元素,它們的次序和值都與數組int_arr完全一樣。

亦可使用數組的一部分來初始化vector對象:

vector<int> subVec(int_arr + 1, int_arr + 4);
// 拷貝三個元素:int_arr[1]、int_arr[2]、int_arr[3]


直接初始化與拷貝初始化

在這里插入圖片描述

  • 使用直接初始化時,編譯器進行函數匹配來選擇與我們提供的參數最匹配的構造函數。
  • 使用拷貝初始化時,編譯器將右側運算對象拷貝到正在創建的對象中,按需選擇是否進行類型轉換。

拷貝初始化通常使用拷貝構造函數來完成。

拷貝初始化不僅在我們用 = 定義變量時會發生,也會在下列情況中發生:

  1. 將一個對象作為實參傳遞給一個非引用類型的形參
  2. 從一個返回類型為非引用類型的函數返回一個對象
  3. 用花括號列表初始化一個數組中的元素或一個聚合類中的成員

拷貝初始化受到explicit類型的構造函數的限制:

需要類型轉化的拷貝初始化的過程是這樣的:

  1. 隱式調用給定類型的構造函數,將等號右邊的值作為實參傳遞給構造函數,從而生成一個臨時的給定類型的對象。(類型轉換通過本步完成,如果是無需類型轉換的拷貝初始化則沒有本步)
  2. 再讓想要初始化的對象隱式調用拷貝構造函數,并將臨時對象作為實參傳給拷貝構造函數,從而完成初始化。

這樣的操作對于explicit類型的構造函數是行不通的,因為無法隱式調用一個explicit的構造函數生成一個臨時對象。 換言之,explicit類型的構造函數是抑制隱式類型轉換的。

因此面對explicit類型的構造函數只能執行直接初始化、或者是無需類型轉換的拷貝初始化:

// error:vector接受大小參數的構造函數是explicit的
vector<int> vi = 10; vector<int> vi(10); // 正確:直接初始化string s(10, 'x'); // 正確:直接初始化
string s1 = s; // 正確:無需類型轉換的拷貝初始化

如果希望使用有給explicit的構造函數,必須顯式地使用:

void f(vector<int>); // f的參數進行拷貝初始化
f(10); 
// error:不能隱式地使用explicit的構造函數構造一個臨時vector
// 因為無法執行從int(也就是10的類型)到vector的類型轉換
f(vector<int>(10)); 
// 正確:顯式地使用explicit的構造函數
// 為vectoc的構造函數傳入10作為實參,構造一個臨時的vector
// 用臨時的vector初始化f

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443836.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443836.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443836.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

js動態增加,刪除td,tr,table,div

js實現的動態添加&#xff0c;刪除table內容&#xff1a; 截圖如下&#xff1a; 1. 2. 源代碼&#xff1a; main.css body {background-image: url(../images/qiantai/bg.png);font-family: arial;font-size: 12px;color: #d4d7da;text-align: center;background-repeat: r…

string類的相關知識及部分操作

文章目錄string對象的初始化string::size_type類型string對象的讀寫操作使用標準庫中的iostream使用getline讀取一整行string對象的比較操作string對象的相加操作兩個string對象相加字面值和string對象相加string對象的初始化 拷貝初始化(copy initialization)&#xff1a;使用…

數組的部分練習

3.27&#xff1a;假設txt_size是一個無參數的函數&#xff0c;它的返回值是int。請回答下列哪個定義是非法的&#xff1f;為什么&#xff1f; unsigned buf_size1024; &#xff08;a&#xff09;int ia[buf_size];  &#xff08;b&#xff09;int ia[4*7-14]; &#xff08…

關于范圍for語句的使用

文章目錄使用范圍for語句處理多維數組使用范圍for語句處理多維數組 舉個例子&#xff0c;使用范圍for語句輸出多維數組&#xff08;ia&#xff09;所有值&#xff1a; for (const auto &row : ia)for (auto col : row)cout << col << endl;本循環中并沒有任何…

vector的應用練習

文章目錄編寫一段程序&#xff0c;使用條件運算符從vector< int >中找出哪些元素的值是奇數&#xff0c;然后將奇數值翻倍。 #include <iostream> #include <ctime> #include <vector> using namespace std; typedef int int_array[4]; int main() {v…

sizeof運算符運算結果小匯

文章目錄sizeof運算符的結果部分地依賴于其作用的類型sizeof運算符的結果部分地依賴于其作用的類型 對char或者類型為char的表達式執行sizeof運算&#xff0c;結果得1對引用類型執行sizeof運算得到被引用對象所占空間的大小對指針執行sizeof運算得到指針本身所占空間的大小對解…

jQuery實現復選框的全選和反選:

jQuery實現復選框的全選和反選&#xff1a; 截圖如下&#xff1a; 代碼如下&#xff1a; index.jsp: <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <%String path request.getContextPath();String basePath…

C語言隱式/顯式類型轉換 | C++四種強制類型轉換、類的隱式轉換、explicit

文章目錄C語言類型轉換隱式類型轉換顯式類型轉換C 強制類型轉換static_castreinterpret_castconst_castdynamic_cast類的隱式類型轉換概念只允許一步類類型轉換explicit 抑制構造函數定義地隱式轉換可以通過顯式轉換使用explicit構造函數C語言類型轉換 隱式類型轉換 編譯器在…

string對象和C風格字符串

混用string對象和C風格字符串 我們都知道允許使用字符串字面值來初始化string對象&#xff1a; string s("Hello World!");C規定&#xff0c;任何出現字符串字面值的地方都可以用以空字符結束的字符數組來替代&#xff1a; 允許使用以空字符結束的字符數組來初始化…

函數重載、引用再探、內聯函數

文章目錄函數重載為什么C支持重載&#xff0c;C語言不支持呢&#xff1f;extern “C”引用再探引用的特性引用的使用場景引用和指針引用和指針的不同點:內聯函數什么是內聯函數&#xff1f;內聯函數的特性內聯函數的好處類的內聯成員函數的聲明內聯函數的使用constexpr函數概念…

類的概念、成員函數的定義方式、類的訪問控制和封裝、類的大小、this指針

文章目錄類的概念structclassclass和struct的區別是什么呢&#xff1f;類中成員函數的兩種定義方式聲明和定義都在類中聲明和定義分離類的訪問控制和封裝類的封裝特性類的大小結構體內存對齊規則類的存儲方式this指針類的概念 在C中&#xff0c;類可以說是最重要的東西&#x…

jQuery實現兩個列表框的值之間的互換:

jQuery實現兩個列表框的值之間的互換&#xff1a; 截圖如下&#xff1a; 代碼如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <%String path request.getContextPath();String basePath reque…

類的6個默認成員函數:構造函數、析構函數、拷貝構造函數、重載運算符、三/五法則

文章目錄6個默認成員函數構造函數概念默認構造函數的類型默認實參概念默認實參的使用默認實參聲明全局變量作為默認實參某些類不能依賴于編譯器合成的默認構造函數第一個原因第二個原因第三個原因構造函數初始化構造函數里面的“”是初始化嗎&#xff1f;為什么要使用列表初始化…

C++ 類的知識 | 構造函數再探、匿名對象、友元函數、內部類、類的const成員、類的static成員

文章目錄構造函數再探以下代碼共調用多少次拷貝構造函數委托構造函數概念形式匿名對象友元友元的聲明友元類令成員函數作為友元函數重載和友元注意內部類特性類的const成員可變數據成員類的static成員概念關于static靜態成員的類內初始化靜態成員能用于某些普通成員不能的場景構…

截取全部數值字符并將其轉化為數值類型

功能 從name中找出全部數值字符&#xff0c;之后將name&#xff08;string類&#xff09;轉為d&#xff08;double類&#xff09; 代碼 #include <iostream> #include <list> #include <deque> #include <vector> #include <forward_list> #i…

替換string中的部分字符

功能 向函數fun中傳入三個參數&#xff1a;將s中所有oldval替換為newval 代碼 #include <iostream> #include <list> #include <deque> #include <vector> #include <forward_list> #include <array> using namespace std;void fun(str…

順序容器(vector、list、string、deque、forward_list)及迭代器、容器適配器

文章目錄概述所有容器都支持的操作迭代器迭代器支持的操作迭代器支持的算術運算容器類型size_typeiterator 和 const_iterator容器定義和初始化拷貝初始化順序容器獨有的構造函數&#xff08;array除外&#xff09;array的初始化與內置數組類型的區別6種初始化方法&#xff08;…

jQuery實現表格隔行換顏色:

jQuery實現表格各行換顏色&#xff1a; 截圖如下&#xff1a; 代碼如下&#xff1a; <span style"font-family:Microsoft YaHei;font-size:14px;"><% page language"java" import"java.util.*" pageEncoding"UTF-8"%> &…

用stack處理中綴表達式【+、-、*、/、()】

文章目錄題目描述思路使用getline()存儲輸入的字符串邊讀取邊壓棧完整代碼題目描述 使用stack處理括號化的表達式。當你看到一個左括號&#xff0c;將其記錄下來。當你在一個左括號之后看到一個右括號&#xff0c;從stack中pop對象&#xff0c;直至遇到左括號&#xff0c;將左括…

原地置換法尋找數組中重復的數

文章目錄題目描述代碼實現題目描述 在一個長度為 n 的數組 nums 里的所有數字都在 0&#xff5e;n-1 的范圍內。數組中某些數字是重復的&#xff0c;但不知道有幾個數字重復了&#xff0c;也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。 輸入&#xff1a; [2,…