C++基本知識點(三)

1.extern和extern “C”

(1)extern

函數的聲明extern關鍵字是可有可無的,因爲函數本身不加修飾的話就是extern的。

下面分變量和函數來說明:
(1) 變量
 extern int a; //聲明一個全局變量
 int a; //定義一個全局變量

extern int a = 0;//定義全局變量並給初值
 int a = 0; //定義全局變量並給初值
上面的四個只有第一個extern int a纔是聲明,其他的全是定義。
當你要引用一個全局變量時,你就要聲明extern int a;這個時候extern不能省,否則就成定義了。

(2) 函數
函數也有聲明和定義,但由於函數的聲明和定義是有區別的,函數的定義是有函數體的,所以函數的聲明和定義都可以將extern省略掉,反正其他文件也是知道這個函數是在其他地方定義的。

(2)extern “C”:

extern "C"的主要作用就是爲了能夠正確實現C++代碼調用其他C語言代碼。加上extern "C"後,會指示編譯器這部分代碼按C語言(而不是C++)的方式進行編譯。由於C++支持函數重載,因此編譯器編譯函數的過程中會將函數的參數類型也加到編譯後的代碼中,而不僅僅是函數名;而C語言並不支持函數重載,因此編譯C語言代碼的函數時不會帶上函數的參數類型,一般只包括函數名。
  作爲C語言的擴展,C++保留了一部分過程式語言的特點,因而它可以定義不屬於任何類的全局變量和函數。但是,C++畢竟是一種面向對象的設計語言,爲了支持函數的重載,C++對全局函數的處理方式有着明顯的不同。
  首先看一下C++對類似C的函數是怎樣編譯的:
  作爲面向對象的語言,C++爲了支持函數重載,函數在被C++編譯後在符號庫中的名字與C語言的不同。假如某個函數的原型爲void foo(int x, int y);該函數被C編譯器編譯後在符號庫中的名字爲_foo,而C++編譯器則會產生_foo_int_int之類的名字。_foo_int_int這樣的名字是包含了函數名以及形參,C++就是靠這種機制來實現函數重載的。
  被extern “C”修飾的函數或者變量是按照C語言方式編譯和鏈接的,所以可以用一句話來概括extern “C”的真實目的:實現C++與C的混合編程。

2. 頂層const和底層const

首先,const是一個限定符,被它修飾的變量的值不能改變。對於一般的變量來說,其實沒有頂層const和底層const的區別,而只有向指針這類複合類型的基本變量,纔有這樣的區別。

一 如何區分頂層const和底層const
指針如果添加const修飾符時有兩種情況:

1 指向常量的指針:

代表不能改變其指向內容的指針。聲明時const可以放在類型名前後都可,拿int類型來說,聲明時:const int和int const 是等價的。聲明指向常量的指針也就是底層const,下面舉一個例子:
int num_a = 1;
int const *p_a = &num_a; //底層const
//*p_a = 2; //錯誤,指向“常量”的指針不能改變所指的對象

注意:指向“常量”的指針不代表它所指向的內容一定是常量,只是代表不能通過解引用符(操作符*)來改變它所指向的內容。上例中指針p_a指向的內容就不是常量,可以通過賦值語句:num_a=2; 來改變它所指向的內容。

2 指針常量:

代表指針本身是常量,聲明時必須初始化,之後它存儲的地址值就不能再改變。聲明時const必須放在指針符號*後面,即:*const 。聲明常量指針就是頂層const,下面舉一個例子:

int num_b = 2;
int *const p_b = &num_b; //頂層const
//p_b = &num_a; //錯誤,常量指針不能改變存儲的地址值
其實頂層const和底層const很簡單,一個指針本身添加const限定符就是頂層const,而指針所指的對象添加const限定符就是底層const。

引用和指針

指針指向一塊內存,它的內容是內存中的地址,而引用則是某個內存的別名,它的指向不改變。
①從現象上看,指針在運行時可以改變其所指向的值,而引用一旦和某個對象綁定後就不再改變。這句話可以理解爲:指針可以被重新賦值以指向另一個不同的對象。但是引用則總是指向在初始化時被指定的對象,以後不能改變,但是指定的對象其內容可以改變。
②從內存分配上看,程序爲指針變量分配內存區域,而不爲引用分配內存區域,因爲引用聲明時必須初始化,從而指向一個已經存在的對象。引用不能指向空值。
引用的效率高。

const修飾成員函數

(1)const修飾的成員函數不能修改任何的成員變量(mutable修飾的變量除外)

(2)const成員函數不能調用非const成員函數,因爲非const成員函數可以會修改成員變量

#include <iostream>
using namespace std;
class Point{
    public :
    Point(int _x):x(_x){}

    void testConstFunction(int _x) const{

        ///錯誤,在const成員函數中,不能修改任何類成員變量
        x=_x;

        ///錯誤,const成員函數不能調用非onst成員函數,因爲非const成員函數可以會修改成員變量
        modify_x(_x);
    }

    void modify_x(int _x){
        x=_x;
    }

    int x;
};
#include<iostream>
using namespace std;
  	int i=3;
	void func(int *p){
	i=5;
	p=&i;
	}

int main(int argc,char * argv[]){
	int j=10;
	int *ptr=&j;
	func(ptr);
	cout<<*ptr<<endl;
}

解析:在func中,拷貝了ptr的值,因此func中的指針p是指針ptr的一份拷貝,改變p的指向不影響ptr的指向。因此返回後ptr仍然指向j。

3.c++ 11新特性:

C++11相比C++98增加了許多關鍵字及新的語法特性。
auto關鍵字

nullptr關鍵字

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
    void TestWork(int index)
    {
        std::cout << "TestWork 1" << std::endl;
    }
    void TestWork(int * index)
    {
        std::cout << "TestWork 2" << std::endl;
    }
};

int main()
{
    Test test;
    test.TestWork(NULL);
    test.TestWork(nullptr);
}

在這裏插入圖片描述
for循環語法

//示例代碼1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int main()
{
    int numbers[] = { 1,2,3,4,5 };
    std::cout << "numbers:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
}

STL容器:
std::array
std::forward_list:std::forward_list爲從++新增的線性表,與list區別在於它是單向鏈表
std::unordered_map:std::unordered_map與std::map用法基本差不多,但STL在內部實現上有很大不同,std::map使用的數據結構爲二叉樹,而std::unordered_map內部是哈希表的實現方式,哈希map理論上查找效率爲O(1)。但在存儲效率上,哈希map需要增加哈希表的內存開銷。

多線程:
在C++11以前,C++的多線程編程均需依賴系統或第三方接口實現,一定程度上影響了代碼的移植性。C++11中,引入了boost庫中的多線程部分內容,形成C++標準,形成標準後的boost多線程編程部分接口基本沒有變化,這樣方便了以前使用boost接口開發的使用者切換使用C++標準接口,把容易把boost接口升級爲C++接口。

std::thread
std::atomic:從功能上看,簡單地說,原子數據類型不會發生數據競爭,能直接用在多線程中而不必我們用戶對其進行添加互斥資源鎖的類型。從實現上,大家可以理解爲這些原子類型內部自己加了鎖。
std::condition_variable:C++11中的std::condition_variable就像Linux下使用pthread_cond_wait和pthread_cond_signal一樣,可以讓線程休眠,直到別喚醒,現在在從新執行。線程等待在多線程編程中使用非常頻繁,經常需要等待一些異步執行的條件的返回結果。

4.getMemory()

4.1

char *getMemory(void)
{ 
    char p[]="hello world";
    return p; 
}
 
void Test(void)
{ 
    char *str=NULL; 
    str=getMemory(); 
    printf(str); 
}

運行結果:運行無誤,但打印亂碼
解釋:getMemory(void)中的p[]爲函數內的局部自動變量,在函數返回後,內存已經被釋放。如果一步步調試,會發現執行str=getMenory();後str不再是NULL了,但是str的內容並不是hello world,而是垃圾數據。

4.2

void getMemory(char **p,int num)
{
    *p=(char *)malloc(num);
}
 
void Test(void)
{
    char *str=NULL;
    getMemory(&str,100);
    strcpy(str,"hello world"); 
    printf(str); 
}

又會內存泄漏,同時還要檢查內存是否有分配成功。

5 函數參數讀取順序:


int main()  
 
{  
 
int arr[] = {6,7,8,9,10};  
 
int *ptr = arr;  
 
*(ptr++) += 123;  
 
printf("%d ,%d/n",*ptr,*(++ptr));  
 
return 0;  
 
}

int arr[]={6,7,8,9,10};

int *ptr=arr;//現在ptr指向6

*(ptr++)+=123;//現在ptr指向7,第一個元素變爲129

printf("%d,%d",ptr,(++ptr)); //考慮從右往左計算,先是*(++ptr),現在ptr指向8,然後*ptr也是8,輸出8,8
printf的參數,函數printf從左往右讀取,然後將先讀取放到棧底,最後讀取的放在棧頂,處理時候是從棧頂開始的,所有從右邊開始處理的。

6 C C++ 結構體 以及C++中的類的區別

6.1 C C++ 結構體區別

C語言中的結構體不能爲空。
C語言中的結構體只是一個複雜的數據類型,只能定義成員變量,不能定義成員函數,但是可以定義函數指針;C++可以定義成員函數和成員變量。C語言的結構體數據成員不能是結構體或者類,只能是簡單的數據成員,而C++的數據成員可以使結構體或類。
C++的結構體和類體現了數據結構和算法的結合。
C語言中定義結構體變量時,struct不可以省略:C++中定義結構體變量時,struct可以省略。

struct Test
{
    int num;
    char ch;
};
 
int main()
{
    //C語言中
    struct Test t1;
    //C++中
    Test t2;
 
    return 0;
}

6.2 C++ 結構體類的區別

對於成員訪問權限以及繼承方式,class中默認private,struct中默認是public。
class可以用於表示模板類型,struct不行

//用模板的時候可以寫
template <class Type>
template <typemname Type>
//不能寫
template<struct Type>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章