1. 程式人生 > >【C++】auto關鍵字(c++11)

【C++】auto關鍵字(c++11)

  • 概念

C++11中,auto不再是一個儲存型別指示符,而是一個自動推導變數的型別,如:

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

int TestAuto()
{
	return 10;
}

int main()
{
	int a = 10;
	auto b = a;//由a是int,可以推匯出b的型別是int
	auto c = 'a';//由‘a’推匯出c的型別是char
	auto d = TestAuto();
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;

    auto e;//這條語句編譯不通過,使用auto定義變數時,必須對其進行初始化
	system("pause");
	return 0;
}
//typeid(b).name()是列印型別名稱的函式

執行結果:

需要注意的是:

使用auto定義變數時,必須對其進行初始化,因為auto並非是一種型別的宣告,而是一個型別宣告時的“佔位符”,編譯器在編譯期間會將auto替換為變數實際的型別。

  • auto使用規則

1.auto與指標和引用結合起來使用。用auto宣告指標型別肘,用auto和auto*沒有任何區別,但用auto宣告引用型別吋必須加&

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

int main()
{
	int x = 10;
	auto a = &x;
	auto* b = &x;
	auto& c = x;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	
	system("pause");
	return 0;
}

2.在同一行定義多個變數時,這些變數必須是相同的型別,否則編譯器將會報錯,因為編譯器實際只對第一個型別進行推導,然後用推匯出來的型別定義其它變數。

#include <iostream>
using namespace std;

void TestAuto()
{
	auto a = 1, b = 2;
	//auto c = 3, d = 4.0;//錯誤
	auto c = 3, d = 4;//正確
	cout << c << endl;
	cout << d << endl;
}

int main()
{
	TestAuto();
	
	system("pause");
	return 0;
}
  • auto不能自動推導的場景

1.auto不能作為函式的引數

void TestAuto(auto a)//此處程式碼不通過,auto不能作為形參型別,因為編譯器無法對a的實際型別進行自動推導
{
    ;
}

2.auto不能用來直接宣告陣列

void TestAuto()
{
	int a[] = { 1, 2, 3 };
	auto b[3] = a;//auto型別不能出現在頂級陣列型別中
}

3.為了避免與c++98中的auto發生混淆,c++11中只保留了auto作為型別推導的用法

4.auto最常見的就是會跟c++11中的新式for迴圈,還有lambda表示式進行配合使用

#include <iostream>
using namespace std;

int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	for (auto e : array)//依次取array裡面的元素讀給e
		cout << e << endl;
	
	system("pause");
	return 0;
}

執行結果:

5.auto不能定義類的非靜態成員變數

6.例項化模板時不能使用auto作為模板引數

  • 基於範圍的新式for迴圈(c++11)

在C++98中如果要遍歷一個數組,可以按照以下方式進行:

void TestFor()
{
intarray[]={1,2,3,4,5};
for (int i = 8; i < sizeof(array) / sizeof(array[0]); ++i )
   array[i] *= 2;

for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
  cout << *p << endl;

}

對於一個有範圍的集合而言,由程式設計師來說明迴圈的範圍是多餘的,有時候還會容易犯錯誤。因此C++11中引入了基於範圍的for迴圈。for迴圈後的括號由冒號”:”分為兩部分:第一部分是範圍內用於迭代的變數,第二部分則表示被迭代的範圍。

 

int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	for (auto e : array)//依次取array裡面的元素讀給e
		cout << e << " ";
	
	system("pause");
	return 0;
}

如果此時想要列印array陣列元素的2倍時,就必須加引用&:

#include <iostream>
using namespace std;

int main()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for(auto& e : array)//遍歷陣列,給陣列的每個物件*2;
        e = e * 2;

    for (auto& e : array)//遍歷陣列,依次取出數組裡面的元素,賦給e
        cout << e << " ";
    system("pause");
    return 0;
}

分析:不加“&”的意思是取出array裡面的每個物件,賦給e,e雖然被改變了,但數組裡面的物件並沒有變;加“&”表示e是數組裡面 每個物件的別名,此時e一改變,數組裡面的每個物件也會改變,因為e是每個物件的別名。

注意:與普通迴圈類似,可以用continue來結束本次迴圈, 也可以用break來跳出整個迴圈。

 

  • for的使用條件

1. for迴圈迭代的範圍必須是確定的,對於陣列而言,就是陣列中第一個元素和最後一個元素的範圍;對於類而言,應該提供begin和end的方法,begin和end就是for迴圈迭代的範圍,以下程式碼就有問題:

void Test(int array[])
{
	for (auto& e : array)
		cout << e <<endl;
}

分析:這個程式碼編譯不通過,此時的array不再是陣列而是一個指標,for的範圍不確定。

2.for迴圈要支援迭代器,還得支援++和==。

  • nullptr(c++11)

 

在C/C++程式設計習慣中,宣告一個變數時最好給該變數一個合適的初始值,否則可能會出現不可預料的錯誤,比如未初始化的指標。如果一個指標沒有合法的指向,我們基本都是按照如下方式對其進行初始化:

void TestPtr(){

int* p1 = NULL;int* p2 = 0;

而NULL實際是一個巨集, 在傳統的C標頭檔案(stddef.h)中,可以看到如下程式碼:

 

#ifndef NULL
#ifdef_ .cplusplus
#define NULL  0
#else
#define NULL ((void *)0)
#endif
#endif

我們發現NULL在c++中被定義為整形常量0,在c語言中被定義為無型別指標(void* )的常量。這種定義,在使用空值的指標時,都不可避免的會遇到一些麻煩,比如:

#include <iostream>
using namespace std;


void f(int)
{
	cout << "f(int)" << endl;
}
void f(int*)
{
	cout << "f(int*)" << endl;
}
int main()
{
	f(0);//調f(int)
	f(NULL);//調f(int)
	f((int*)NULL);//調f(int*)
	system("pause");
	return 0;
}

執行結果:

這個程式的本意是:想通過f(NULL)呼叫fint*)函式,但是由於NULL被定義成0,因此與程式的初衷相悖。在C++98中,字面常量0既可以是一個整形數字, 也可以是無型別的指標(Void)常量, 但是編譯器預設情況下將其看成是一個整形常量, 如果要將其按照指標方式來使用,必須對其進行強轉(void *)0。

  • nullptr

為了考慮相容性,C++11並沒有消除常量0的二義性,而是給出了全新的nullptr,表示空值指標。C++11為什麼不在NULL的基礎上進行擴充套件,這是因為NULL以前就是一個巨集, 而且不同的編譯器廠商對於NULL的實現可能不太相同,而且直接擴充套件NULL,可能會影響以前舊的程式。因此:為了避免混淆,C++11提供了nullptr,即: nullptr代表- 個指標空值常量。nullptr是有型別的, 其型別為nullptr. _t,僅僅可以被隱式轉化為指標型別,nullptr. _t被定義在標頭檔案中:

typedef decltype(nullptr) nullptr_ t;

需要注意的是:

1.在使用nullptr表示指標空值時,不需要包含標頭檔案,因為nullptr是C++11作為新關鍵字引入的。

2.在C++11中,sizeof(nullptr) 與sizeof(void*)0)所佔的位元組數相同。

3.為了提高程式碼的健壯性,建議在後續表示指標空值時,最好使用nullptr。

這裡還有常見的一類題:

請區分:NULL、0、'\0'、"\0"?

NULL:是被定義出來的一個巨集,它不是關鍵字,值為0;

0:是整形的0,值為0;

'\0':是字元0,值為0;

"\0":是字串;

計算:

#include <iostream>
using namespace std;
int main()
{
    char str[] = "\0";
    cout << strlen(str)<< endl;//0位元組
    cout << sizeof(str) << endl;//2個位元組
    system("pause");
    return 0;
}

其中strlen(str)大小為0,因為轉義字元\+0,就是字串中的結束標誌"\0",只要遇到"\0",它就自動停下來了,所以strlen(str)大小為0;sizeof(str)大小為2個位元組,轉義字元\+0是一個字元,還有字串結束標誌的那個"\0",加起來就是2個位元組。

再比如:

#include <iostream>
using namespace std;

int main()
{
    char str[] = "\\0";
    cout << strlen(str)<< endl;//2個位元組
    cout << sizeof(str) << endl;//3個位元組
    system("pause");
    return 0;
}

這個程式裡面strlen(str)大小為2個位元組,轉義字元\+\轉義為\,後面還有個數字0,總共是2個位元組;sizeof(str)大小為3個位元組。