C++中的關鍵字

一、const

  被const修飾的變量或者函數具有“只讀屬性”,const可以修飾變量、作爲函數參數、修飾成員函數,修飾函數返回值。

1、修飾變量

  修飾變量時,變量的值不可以被修改

const int a; // a的值不允許被改變
const int *a; // 指針a指向的內容可以改變,但是不能改變*a的值
int * const a; // 指針a指向的內容不可以改變,但是可以改變*a的值
const int * const a; // 指針a指向的內容不可以改變,也不可以改變*a的值
  • 1
  • 2
  • 3
  • 4

2、修飾函數參數

  修飾函數參數時,表示不能修改參數的值或者指針所指向的內容,也可以作爲重載的標誌(有const只讀,沒有const讀寫都可以)

int fun(int a);
int fun(const int &a);
  • 1
  • 2

3、修飾函數/成員函數

  修飾成員函數時,表示不能修改類的數據成員並且不能調用非const函數

#include <iostream>  
using namespace std;  

class A{
private:
int i;
public:
// set函數需要設置i的值,所以不能聲明爲const
void set(int n){
i = n;
}
// get函數返回i的值,不需要對i進行修改,則可以用const修飾。防止在函數體內對i進行修改
int get() const{
return i;
}
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4、修飾函數返回參數

  修飾函數返回參數時,表明函數返回值不可以被修改,即使得函數調用表達式不能作爲左值。

const int fun(const int &a);
  • 1

二、mutable

  mutalbe的意思是“可變的,易變的”。在C++中,mutable也是爲了突破const的限制而設置的。被mutable修飾的變量(mutable只能由於修飾類的非靜態數據成員),將永遠處於可變的狀態,即使在一個const函數中

1、mutable的應用場景

  一個常用的場景是計算某個函數被調用的次數

#include <iostream>

class Person {
public:
Person();
~Person();

<span class="token keyword">int</span> <span class="token function">getAge</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span><span class="token punctuation">;</span> <span class="token comment">/*調用方法*/</span>
<span class="token keyword">int</span> <span class="token function">getCallingTimes</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span><span class="token punctuation">;</span> <span class="token comment">/*獲取上面的getAge()方法被調用了多少次*/</span>

private:
int age;
char *name;
float score;
mutable int m_nums; /用於統計次數/
};

Person::Person()
{
m_nums = 0;
}

Person::~Person(){}

int Person::getAge() const
{
std::cout << “Calling the method” << std::endl;
m_nums++;
// age = 4; 仍然無法修改該成員變量
return age;
}

int Person::getCallingTimes()const
{
return m_nums;
}

int main()
{
Person *person = new Person();
for (int i = 0; i < 10; i++) {
person->getAge();
}
std::cout << “getAge()方法被調用了” << person->getCallingTimes() << “次” << std::endl;
delete person;

<span class="token function">getchar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

三、static

1、限制變量的作用域(隱藏)

  沒有加static關鍵字的變量和函數具有全局可見性,加了static後,就會對其他源文件隱藏

2、保持變量內容的持久性

  被static修飾的變量在程序剛開始運行時就會初始化,並存儲在靜態存儲區(還有全局變量),生存期爲整個源程序

#include <stdio.h>

int fun()
{
// 在第一次進入這個函數的時候,變量a被初始化爲10!並接着自減1,以後每次進入該函數就
// 不會被再次初始化了,僅進行自減1的操作;在static發明前,要達到同樣的功能,
// 則只能使用全局變量:
static int count = 10;
return count;
}
int main(void)
{
printf(“global\t\tlocal static\n”);
for(; count <= 10; ++count)
printf("%d\t\t%d\n", count, fun());
return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3、默認初始化爲0

  其實全局變量也具備這一屬性,因爲全局變量也存儲在靜態數據區。在靜態數據區,內存中所有的字節默認值都是0x00,某些時候這一特點可以減少程序員的工作量

#include <stdio.h>
int a;
int main()
{
     int i;
     static char str[10];
     printf("integer: %d; string: (begin)%s(end)", a, str);
     return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4、C++中的類成員聲明static

  在類中聲明static變量或者函數時,初始化時使用作用域運算符來標明它所屬類,因此,靜態數據成員是類的成員,而不是對象的成員,這樣就出現以下作用:

  • 類的靜態成員函數是屬於整個類而非類的對象,所以它沒有this指針,這就導致它僅能訪問類的靜態數據和靜態成員函數

  • 不能將靜態成員函數定義爲虛函數

  • 靜態數據成員是靜態存儲的,所以必須對它進行初始化(在類外進行),<數據類型><類名>::<靜態數據成員名>=<值>

  • 靜態成員函數可以通過類名::靜態成員函數的方式進行訪問,不需要實例化

四、extern

  extern "C"的作用是在C++編譯器中調用被C語言編譯器編譯過的函數

  爲什麼要這麼做呢?原因是C++中支持函數重載但是C語言中不支持,而且C++的編譯器會對程序中符號進行修飾

/* file: test_extern_c.h */
#ifndef __TEST_EXTERN_C_H__
#define __TEST_EXTERN_C_H__

#ifdef __cplusplus
extern “C” {
#endif

<span class="token keyword">extern</span> <span class="token keyword">int</span> <span class="token function">ThisIsTest</span><span class="token punctuation">(</span><span class="token keyword">int</span> a<span class="token punctuation">,</span> <span class="token keyword">int</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span>

#ifdef __cplusplus
}
#endif
#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
/* test_extern_c.c */
#include "test_extern_c.h"

int ThisIsTest(int a, int b)
{
return (a + b);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
/* main.cpp */
#include "test_extern_c.h"
#include <stdio.h>
#include <stdlib.h>

class FOO {
public:
void bar(int a, int b)
{
printf(“result=%i/n”, ThisIsTest(a, b));
}
};
int main(int argc, char argv)
{
int a = atoi(argv[1]);
int b = atoi(argv[2]);
FOO *foo = new FOO();
foo->bar(a, b);
return(0);
}

// 編譯:
gcc -c test_extern_c.c
g++ main.cpp test_extern_c.o -o main
./main 1 2

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

五、volatile

  volatile的作用: 作爲指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。簡單地說就是防止編譯器對代碼進行優化。當要求使用volatile 聲明的變量的值的時候,系統總是重新從它所在的內存讀取數據,而不是使用保存在寄存器中的備份。即使它前面的指令剛剛從該處讀取過數據。而且讀取的數據立刻被保存。

  volatile跟多線程無關,它不是一種同步手段,用它來實現線程安全是錯的。原子和鎖會保證線程安全性

  • 易變性:兩條語句,下一條語句不會直接使用上一條語句對應的volatile變量的寄存器內容,而是重新從內存中讀取。

  • 不可優化:volatile告訴編譯器,不要對我這個變量進行各種激進的優化,甚至將變量直接消除,保證程序員寫在代碼中的指令,一定會被執行。

  • 順序性:保證volatile變量間的順序性,編譯器不會進行亂序優化

1、面試題—>一個參數可以即是const又是volatile嗎?

  可以,一個例子是隻讀狀態寄存器。

六、inline

  使用inline聲明的函數叫做內聯函數,原理是將函數調用替換爲函數實體(編譯期),不需要中斷調用,減少了函數參數壓棧過程,增加了函數調用的效率。但是有幾點需要注意:

  • 內聯函數只適合體積較小(一般十行以內),沒有循環、遞歸和開關操作的函數;

  • 在類內定義的成員函數都是內聯函數;

  • 過多使用內聯函數可能會增加程序體積減少高速緩存命中率

1、面試題—>C++中哪些函數不能聲明爲inline?

  • 包含了遞歸、循環等結構的函數一般不會被內聯

  • 虛函數一般不會內聯,但是如果編譯器能在編譯時確定具體的調用函數,那麼仍然會就地展開該函數。

  • 如果通過函數指針調用內聯函數,那麼該函數將不會內聯而是通過call進行調用。

  • 構造和析構函數一般會生成大量代碼,因此一般也不適合內聯

  • 如果內聯函數調用了其他函數也不會被內聯。

如果想要阻止某函數被內聯,可以在函數體前加上 __attribute__((noinline))

七、explicit

  關鍵字explicit只對一個實參的構造函數有效,只能在類內定義,它表示禁止對構造函數進行隱式轉換。當我們用explicit關鍵字聲明構造函數時,它將只能以直接初始化的形式使用,而不能使用賦值初始化。

explicit A(const string &s):a(s) {};
A a("abc");//直接初始化。
a.fun(static_cast<A>(cin));//強制執行顯式轉換。
  • 1
  • 2
  • 3

八、#define

  #define表示宏定義,在預處理階段,進行簡單的文本替換,沒有類型檢查,沒有分配內存空間,不能調試(這是一個缺點,所以建議儘可能使用const替換#define)

  • #define不能以分號結束,儘量使用括號
  • 宏定義不是函數!
// 比較兩個數的較小者
#define MIN(a, b) ( (a) <= (b) ? (a) : (b) )

// 返回一年有多少天
#define SECONDS_PER_YEAR (365 * 24 * 60 * 60)UL

  • 1
  • 2
  • 3
  • 4
  • 5

在這裏插入圖片描述

1、面試題—>內聯函數和宏定義的區別?

  • 宏定義不是函數,只是在預處理階段進行簡單的文本替換;而內聯函數是在編譯期將函數調用替換爲函數實體

  • 宏定義沒有參數類型檢查,而內聯函數會進行檢查

九、typedef

  typedef表示爲參數定義一個別名,具有一定的封裝性

typedef (int*) pInt1;
#define pInt2 int*

pInt1 a, b; // a和b都是int類型的指針
pInt2 a, b; // a是int類型的指針,但是b只是一個int變量

  • 1
  • 2
  • 3
  • 4
  • 5

十、sizeof

  sizeof返回的是變量所佔用的字節數,是運算符而不是函數,常見的sizeof如下所示:

// 基本數據類型的sizeof
cout<<sizeof(char)<<endl;                    //結果是1
cout<<sizeof(int)<<endl;                     //結果是4
cout<<sizeof(unsigned int)<<endl;            //結果是4
cout<<sizeof(long int)<<endl;                //結果是4
cout<<sizeof(short int)<<endl;               //結果是2
cout<<sizeof(float)<<endl;                   //結果是4
cout<<sizeof(double)<<endl;                  //結果是8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
// 指針變量的sizeof
char *pc ="abc";
sizeof(pc);     // 結果爲4
sizeof(*pc);      // 結果爲1
int *pi;
sizeof(pi);     //結果爲4
sizeof(*pi);      //結果爲4
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
// 數組的sizeof值等於數組所佔用的內存字節數
char a1[] = "abc";
int a2[3];
sizeof( a1 ); // 結果爲4,字符 末尾還存在一個NULL終止符
sizeof( a2 ); // 結果爲3*4=12(依賴於int)
void foo3(char a3[3]) // 數組作爲函數參數時就變成指針
{
	int c3 = sizeof( a3 ); // c3 == 4
}
void foo4(char a4[])
{
	int c4 = sizeof( a4 ); // c4 == 4
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
// 空結構體的sizeof
// C++編譯器會在空類或空結構體中增加一個虛設的字節(有的編譯器可能不止一個),
// 以確保不同的對象都具有不同的地址。
struct S5 { };
sizeof( S5 ); // 結果爲1
  • 1
  • 2
  • 3
  • 4
  • 5
// 類的sizeof
class A
{
    public:
        int b;
        float c;
        char d;
}; // 內存對齊
int main(void)
{
    A object;
    cout << "sizeof(object) is " << sizeof(object) << endl;
    return 0 ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

1、面試題—>說明sizeof的使用場合?

  • 查看某個對象所佔用的字節數

  • 與存儲分配和I/O系統那樣的例程進行通信

2、面試題—>說明sizeof和strlen?

  • sizeof是運算符而strlen是函數

  • strlen的結果是在運行期計算出來的,結果是字符串的長度而不是所佔用的內存大小。

  • strlen的參數只能是char*,而且必須是以’\0’結尾的。

十一、union

1、union和struct的區別?

  • 結構體:每個成員都擁有自己的內存,互不干涉,同時遵循內存對齊原則一個struct變量的總大小等於所有成員的大小之和

  • 聯合體:各成員共用一塊內存空間,並且同時只有一個成員可以得到這塊內存的使用權(對該內存的讀寫),各變量共用一個內存首地址。

2、union的賦值機制

#include <stdio.h>
union u{
	int a;	
	short b;
};
int main()
{
	union u t;
	t.a=100000;
	t.b=10;
	printf("%d %hd", t.a, t.b); // 輸出結果爲: 65546 10。
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

  給聯合體中的成員賦值時,只會對這個成員所屬的數據類型所佔內存空間的大小覆蓋成後來的這個值,而不會影響其他位置的值。

  成員a爲int類型,b爲short類型,第一次給a賦值100000,二進制爲0000 0000 0000 0001 1000 0110 1010 0000,第二次給b賦值,由於short只佔2個字節,所以只會覆蓋16位二進制,0000 0000 0000 1010,最後的結果是0000 0000 0000 0001 0000 0000 0000 1010(從低位覆蓋),所以輸出結果爲65546。

參考:https://www.cnblogs.com/songdanzju/p/7422380.html

      </div>
      <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-258a4616f7.css" rel="stylesheet">
              </div>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章