C++入門知識(二)

6.引用

1.什麼是引用?

引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會爲引用變量開闢內存空間,它和它引用的變量共用同一塊內存空間。
**類型& 引用變量名(對象名) = 引用實體;**
void TestRef()
{
	int a = 10;
	int& ra = a;//<====定義引用類型
	printf("%p\n", &a);
	printf("%p\n", &ra);
	cout << a << endl;
	cout << ra << endl;
	ra -= 1;
	cout << a << endl;
	cout << ra << endl;
}
int main()
{
	TestRef();
	system("pause");
	return 0;
}

執行結果:

2.引用特性:

  1. 引用在定義時必須初始化
  2. 一個變量可以有多個引用
  3. 引用一旦引用一個實體,再不能引用其他實體
void TestRef2()
{
	int a = 10;
	//int& ra;   // 該條語句編譯時會出錯,定義時沒有初始化
	int& ra = a;
	int& rra = a;
	printf("%p %p %p\n", &a, &ra, &rra);
}

3.常引用

常引用聲明方式:const 類型標識符&引用名=目標變量名;

void TestConstRef()
{
const int a = 10;
//int& ra = a;   // 該語句編譯時會出錯,a爲常量
const int& ra = a;
// int& b = 10; // 該語句編譯時會出錯,b爲常量
const int& b = 10;
double d = 12.34;
//int& rd = d; // 該語句編譯時會出錯,類型不同
const int& rd = d; }

用這種方式聲明的引用,不能通過引用對目標變量的值進行修改,從而使引用的目標成爲const,達到了引用的安全性。

4.引用場景

1、傳遞可變參數
2.做返回值

void Swap(int& left, int& right) {
	int temp = left;
	left = right;
	right = temp;
}
int& TestRefReturn(int& a) {
	a += 10;
	return a;
}
int main()
{
	int a = 8, b = 9; Swap(a, b);
	cout << a << b << endl;
	TestRefReturn(a);
	cout << a << endl;
	return 0;
}

在這裏插入圖片描述
注意:如果函數返回時,離開函數作用域後,其棧上空間已經還給系統,因此不能用棧上的空間作爲引用類型
返回。如果以引用類型返回,返回值的生命週期必須不受函數的限制(即比函數生命週期長)。

5.引用和指針的區別

★ 相同點:
都是地址的概念;
指針指向一塊內存,它的內容是所指內存的地址;引用是某塊內存的別名。
★ 區別:
1. 指針是一個實體,而引用僅是個別名;
2. 引用使用時無需解引用(*),指針需要解引用;
3. 引用只能在定義時被初始化一次,之後不可變;指針可變;
4. 引用沒有 const,指針有 const;
5. 引用不能爲空,指針可以爲空;
6. “sizeof 引用”得到的是所指向的變量(對象)的大小,而“sizeof 指針”得到的是指針本身(所指向的變量或對象的地址)的大小;
7. 指針和引用的自增(++)運算意義不一樣;
8.從內存分配上看:程序爲指針變量分配內存區域,而引用不需要分配內存區域。

7內聯函數

1什麼是內聯函數?

內聯函數(有時稱作在線函數或編譯時期展開函數)是一種編程語言結構,用來建議編譯器對一些特殊函數進行內聯擴展(有時稱作在線擴展);也就是說建議編譯器將指定的函數體插入並取代每一處調用該函數的地方(上下文),從而節省了每次調用函數帶來的額外時間開支。但在選擇使用內聯函數時,必須在程序佔用空間和程序執行效率之間進行權衡,因爲過多的比較複雜的函數進行內聯擴展將帶來很大的存儲資源開支。另外還需要特別注意的是對遞歸函數的內聯擴展可能引起部分編譯器的無窮編譯。

2內聯函數的特性

  1. 內聯函數是一種以空間換時間的做法,省去調用函數額開銷。所以代碼很長或者有循環/遞歸的函數不適宜使用作爲內聯函數。
  2. 內聯函數對於編譯器而言只是一個建議,編譯器會自動優化,如果定義爲inline的函數體內有循環/遞歸等等,編譯器優化時會忽略掉內聯。
  3. 內聯函數不建議聲明和定義分離,分離會導致鏈接錯誤。因爲inline被展開,就沒有函數地址了,鏈接就會找不到。
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i) {
cout << i << endl; }
// main.cpp
#include "F.h"
比特科技
int main()
{ f(10);
return 0; }
// 鏈接錯誤:main.obj : error LNK2019: 無法解析的外部符號 "void __cdecl f(int)" (?
f@@YAXH@Z),該符號在函數 _main 中被引用

8auto關鍵字

auto是一個C/C++語言存儲類型,僅在語句塊內部使用,初始化可爲任何表達式,其特點是當執行流程進入該語句塊的時候初始化可爲任何表達式。

#include<iostream>
using namespace std;
int TestAuto()
{
	return 10;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	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;
}

運行結果:
在這裏插入圖片描述
【注意】使用auto定義變量時必須對其進行初始化,在編譯階段編譯器需要根據初始化表達式來推導auto的實際類型。

auto如何使用?

1. auto與指針和引用結合起來使用
用auto聲明指針類型時,用auto和auto*沒有區別,但用auto聲明引用類型時則必須加&
2. 在同一行定義多個變量
當在同一行聲明多個變量時,這些變量必須是相同的類型,否則編譯器將會報錯,因爲編譯器實際只對第一個類型進行推導,然後用推導出來的類型定義其他變量。

#include<iostream>
using namespace std;
void TestAuto()
{
	auto a = 1, b = 2;
	//auto c = 3, d = 4.0;  // 該行代碼會編譯失敗,因爲c和d的初始化表達式類型不同
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto* e = &a;
	auto& f = a;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(e).name() << endl;
	cout << typeid(f).name() << endl;
	system("pause");
	return 0;
}

在這裏插入圖片描述

以下情況auto不能使用

  1. auto不能作爲函數的參數
    // 此處代碼編譯失敗,auto不能作爲形參類型,因爲編譯器無法對a的實際類型進行推導void TestAuto(auto a) {}
  2. auto不能直接用來聲明數組
void TestAuto()
{
    int a[] = {1,2,3};
    auto b[] = {4,5,6};
}

9基於範圍的for循環

用法:

for循環後的括號由冒號“ :”分爲兩部分:第一部分是範圍內用於迭代的變量,第二部分則表示被迭代的範圍。
語法形式:

for(declaration:expression)
{
	statement
}

其中:
expression部分表示一個對象,用於表示一個序列。
declaration部分負責定義一個變量,該變量將被用於訪問序列中的基礎元素。
每次迭代,declaration部分的變量會被初始化爲expression部分的下一個元素值。

基於範圍的for循環特點

(1)和普通循環一樣,也可以採用continue跳出循環的本次迭代。
(2)用break來終止整個循環

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

int main()
{
	int x[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	for (int y : x) { 
		cout << y << " ";
	}

	for (auto y : x) {
		cout << y << " ";
	}
	cout << endl;

	for (auto &y : x) { 
		cout << y << " ";
	}
	cout << endl;

	for (const auto &y : x) { 
		cout << y << " ";
	}
	cout << endl;
	cout << "end of integer array test" << endl;
	cout << endl;

	vector<double> v;
	for (int i = 0; i < 10; ++i) {
		v.push_back(i + 0.14159);
	}

	for (const auto &j : v) {
		cout << j << " ";
	}
	cout << endl;
	cout << "end of vector test" << endl;
	system("pause");
}

在這裏插入圖片描述

10指針空值nullptr

nullptr:是C++空指針類型的關鍵字,nullptr是在C++11中引入的。
傳統意義上來說,c++把NULL、0視爲同一種東西,有些編譯器將NULL定義爲 ((void*)0),有些將其定義爲0.

c++不允許直接將void隱式的轉化爲其他類型,但是如果NULL被定義爲 ((void)0),
當編譯char p = NULL;NULL只好被定義爲0。
還有:
void func(int);
void func(char
);
如果NULL被定義爲0,func(NULL)會去調用void func(int),這是不合理的
所以引入nullptr,專門用來區分0、NULL。
nullptr的類型爲nullptr_t,能夠隱式的轉換爲任何指針。

nullptr 與 nullptr_t:

typedef decltype(nullptr) nullptr_t; 

注意:

  1. 在使用nullptr表示指針空值時,不需要包含頭文件,因爲nullptr是C++11作爲新關鍵字引入的。
  2. 在C++11中,sizeof(nullptr) 與 sizeof((void*)0)所佔的字節數相同。
  3. 爲了提高代碼的健壯性,在後續表示指針空值時建議最好使用nullptr。

總結,剛接觸C++,整理的知識不是很有條理,有問題的地飯否還望指出!!

發佈了28 篇原創文章 · 獲贊 22 · 訪問量 6280
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章