C/C++知識點

 

 constios

做用c++

  1修飾變量,說明變量不能夠被改變程序員

  2修飾指針,分爲指向常量的指針和指針常量express

  3常量引用,常常用於形參類型,即避免了拷貝,又避免了函數對值的修改數組

  4修飾成員函數,說明該成員函數不能修改爲員變量安全

使用數據結構

 1 //
 2 class A
 3 {
 4   private:
 5    const int a;
 6  public:
 7      // 構造函數
 8     A() { };
 9     A(int x) : a(x) { };        // 初始化列表
10 
11     // const可用於對重載函數的區分
12     int getValue();             // 普通成員函數
13     int getValue() const;       // 常成員函數,不得修改類中的 何
14                                     //數據成員的值     
15 } ; 
16 
17 void function()
18 {
19     // 對象
20     A b;                        // 普通對象,能夠調用所有成員函數
21     const A a;                  // 常對象,只能調用常成員函數、更新常成員變量
22     const A *p = &a;            // 常指針
23     const A &q = a;             // 常引用
24 
25     // 指針
26     char greeting[] = "Hello";
27     char* p1 = greeting;                // 指針變量,指向字符數組變量
28     const char* p2 = greeting;          // 指針變量,指向字符數組常量
29     char* const p3 = greeting;          // 常指針,指向字符數組變量
30     const char* const p4 = greeting;    // 常指針,指向字符數組常量
31 }
32 
33 // 函數
34 void function1(const int Var);           // 傳遞過來的參數在函
35 //數內不可變
36 void function2(const char* Var);         // 參數指針所指內容爲常量
38 void function3(char* const Var);         // 參數指針爲常指針
39 void function4(const int& Var);          // 引用參數在函數內
40 //爲常量
41 
42 // 函數返回值
43 const int function5();      // 返回一個常數
44 const int* function6();     // 返回一個指向常量的指針變量,使用:
46 const int *p = function6();
47 int* const function7();     // 返回一個指向變量的常指針,使用:
48 int* const p = function7();  

staticapp

做用函數

  1 修飾普通變量,修改變量的存儲區域和生命週期,使變量存儲在靜態區,在main函數運行前就分配了空間,若是又初始值就用初始值初始化它,若是沒有初始值系統用默認值初始化它。佈局

  2 修飾普通函數,代表函數的做用範圍,僅在定義該函數的文件內才能使用。在多人開發項目時,爲了防止與他人命名函數重名,能夠將函數定位爲static

  3 修飾成員變量,修飾曾元變量使全部的對象只保存一個變量,並且不須要生成對象就能夠訪問該成員。

  4 修飾成員函數,修飾成員函數使得不須要生成對象就能夠訪問該函數,可是在static函數內不能訪問非靜態成員。

this指針

  1this指針是一個隱含於每一個非靜態成員函數中的特殊指針。它指向正在被該成員函數操做的那個對象。

  2當對一個對象調用成員函數時,編譯程序先將對象的地址賦給this指針,而後調用成員函數,每次成員函數存取數據成員時,由隱含使用this指針

  3當一個成員函數被調用時,自動向他傳遞一個隱含的參數,該參數是一個指向這個成員函數所在的對象的指針。

  4this指針被隱含的聲明爲:className *const this, 這意味這不能給this指針賦值;在ClassName類的const成員函數中,this指針的類型爲:const ClassName* const,這說明不能對this指針所指向的這種對象是不能修改的(即不能對這種對象的數據成員景象賦值操做);

  5 this並非一個常規變量,而是一個右值,因此不能取得this的地址(不能&this)。

  6 在如下場景中,常常須要顯式引用this指針:

  爲實現對象的鏈式引用

  爲避免統一對象進行賦值操做

  在實現一些數據結構時,如list

inline內聯函數

特徵

  至關於把內聯函數裏面的內容卸載調用內聯函數處

  至關於不用指向進入函數 的步驟,直接執行函數體;

  至關於宏,卻比宏多了類型檢查,真正具備函數特徵

  不能包含循環,遞歸,switch等複雜操做;

  在類聲明中定義的函數,除了虛函數的其餘函數都會自動隱式的當成內聯函數。

// 聲明1(加 inline,建議使用)
inline int functionName(int first, int secend,...);
// 聲明2(不加 inline)
int functionName(int first, int secend,...);
// 定義
inline int functionName(int first, int secend,...) {/****/};

// 類內定義,隱式內聯
class A {
    int doA() { return 0; }         // 隱式內聯
}

// 類外定義,須要顯式內聯
class A {
    int doA();
}
inline int A::doA() { return 0; }   // 須要顯式內聯

編譯器對inline函數的處理步驟

  1 將inline函數體複製到inline函數調用點處;

  2 爲所用inline函數中的局部變量分配內存空間

  3將inline函數的輸入參數和返回值映射到調用方法的局部變量空間中;

  4若是inlin函數有多個返回點,將其轉變爲inline函數代碼塊末尾的分支(使用goto)

優缺點

優勢

  1內聯函數同洪函數同樣將在被調用處進行代碼展開,省去了參數壓棧,棧幀開闢與回收,結果返回等,從而提升程序運行速度

  2內聯函數相比宏函數來講,在代碼展開時,會作安全檢查或自動類型轉換(同普通函數),而宏定義則不會。

  3在類中聲明同時定義的成員函數,自動轉化爲內聯函數,所以內聯函數能夠訪問類的成員變量,宏定義則不能。

  4內聯函數在運行時可調試,而宏定義不能夠

缺點

  1代碼膨脹。內聯是以代碼膨脹(複製)爲代價,消除函數調用帶來的開銷。若是執行函數體內代碼的時間,相比於函數調用的開銷較大,那麼效率的收穫會不多。另外一方面,每一處內聯函數的調用都要複製代碼,將使程序的總代碼量增大,消耗更多的內存空間。

  2inlin函數沒法隨着函數庫升級而升級,inlin函數改變須要從新編譯,不像non-inline能夠直接連接。

  3是否內聯,程序員不可控。內聯函數只是對編譯器的建議,是否函數內聯,決定權在於編譯器。

虛函數(virtual)能夠是內聯函數(inline)嗎?

  虛函數能夠是內聯函數,內聯是能夠修飾虛函數的,可是當虛函數表現多態性的時候不能內聯。

  內聯是在編譯器建議編譯器內聯,而虛函數的多態性在運行期,編譯器沒法直到運行期間調用那個代碼,所以虛函數表現爲多態時(運行期)不能夠內聯。

  inline virtal 惟一能夠捏臉的時候是:編譯器知道所調用的對象是那個類(如Base::who()),這隻有在編譯器具備實際對象而不是對象的指針或引用時纔會發生。

 1 #include<iostream>
 2 using namespace std;
 3 class base
 4 {
 5    public:
 6        inline virtual void who()
 7         {
 8            cout<<"i AM BASE\n";
 9     
10          } 
11         virtual ~Base() {}
12 };    
13 
14 class Derived : public Base
15 {
16 public:
17     inline void who()  // 不寫inline時隱式內聯
18     {
19         cout << "I am Derived\n";
20     }
21 };
22 
23 int main()
24 {
25     // 此處的虛函數 who(),是經過類(Base)的具體對象(b)來調用的,編譯期間就能肯定了,因此它能夠是內聯的,但最終是否內聯取決於編譯器。 
26     Base b;
27     b.who();
28 
29     // 此處的虛函數是經過指針調用的,呈現多態性,須要在運行時期間才能肯定,因此不能爲內聯。  
30     Base *ptr = new Derived();
31     ptr->who();
32 
33     // 由於Base有虛析構函數(virtual ~Base() {}),因此 delete 時,會先調用派生類(Derived)析構函數,再調用基類(Base)析構函數,防止內存泄漏。
34     delete ptr;
35     ptr = nullptr;
36 
37     system("pause");
38     return 0;
39 } 

assert()

斷言,是宏,而非函數。assert宏的原型定義在<assert.h>(C)、<assert>(C++)中,其做用是若是它的條件返回錯誤,則終止程序執行。能夠經過NDEBUG來關閉assert,可是須要在源代碼的開頭,include<assert.h>以前。

1 #define NDBUG  //這行可使assert不可用
2 #include<assert.h>
3 assert(p!=NULL);//assert不可用

sizeof()

  sizeof對數組,獲得整個數組所佔空間大小

  sizeof對指針,獲得指針自己所佔空間大小

 

pragme pack(n)

設定結構體,聯合以及類成員變量以n字節方式對其

#pragma pack(push)//保存對齊狀態
#pragma pack(4)//設定爲4字節對齊
struct test
{
    char m1;
    double m4;
    int m3;
    char c1;
};
#pragma pack(pop)//恢復對齊方式

test t1;
cout << sizeof(t1) << endl; //結果爲20

位域

bit mode: 2;//mode 佔 2 位

類能夠將其(非靜態)數據成員定義爲位域(bit-field),在一個位域中含有必定數量的二進制位。當一個程序須要向其餘程序或硬件設備傳遞二進制數據時,一般會用到位域。

  -位域在內存中的佈局時與機器有關的

  -位域的類型必須是整形或枚舉類型,帶符號類型中的位域的行爲將因具體實現而定

  -取地址運算符(&)不能做用於位域,任何指針都沒法指向類的位域;

volatile

volatile int i= 10;

  volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示能夠被某些編譯器未知的因素(操做系統,硬件,其餘線程等)更改。因此使用volatile告訴編譯器不該對這樣的對象經行優化。

  volatile關鍵字聲明的變量,每次訪問時都必須從內存中取出值(沒有被volatile修飾的變量,可能因爲編譯器的優化,從CPU寄存器中取值)

  const 能夠是 volatile(如只讀的狀態寄存器)

  指針能夠是volatile

extern "C"

被extern限定的函數或變量是extern類型的

被extern 「C」修飾的變量和函數是按照C語言方式編譯和連接的

能夠必滿C++由於符號修飾致使代碼不能和C語言庫中的符號進行連接的問題。

#ifdef __cplusplus
extern "C" {
#endif

void *memset(void *, int, size_t);

#ifdef __cplusplus
}
#endif

struct 和 typedef struct

c中

typedet struct student

{

int age;

}s;

等價於

struct student{

  int age;

};

typedef struct Student S;

此時 S 等價於 struct Student,但兩個標識符名稱空間不相同。

另外還能夠定義與 struct Student 不衝突的 void Student() {}

// cpp
struct Student { 
    int age; 
};

void f( Student me );       // 正確,"struct" 關鍵字可省略

2、若定義了與 Student 同名函數以後,則 Student 只表明函數,不表明結構體,以下:

typedef struct Student { 
    int age; 
} S;

void Student() {}           // 正確,定義後 "Student" 只表明此函數

//void S() {}               // 錯誤,符號 "S" 已經被定義爲一個 "struct Student" 的別名

int main() {
    Student(); 
    struct Student me;      // 或者 "S me";
    return 0;
}

C++中的struct和class

總的來講,struct更合適當作是一個數據結構的實現體,class更適合當作是一個對象的實現體;

區別

  最本質的一個區別就是默認的訪問權限

    默認的繼承訪問權限。struct是public的,class是private

    struct做爲數據結構的實現體,

union 聯合

聯合union是一種節省空間的特殊的類,一個union能夠由多個數據成員,可是在任意時刻只能由一個數據成員有值。當某個成員被賦值後其餘成員變爲未定義狀態。聯合有以下特色:

  -默認訪問控制符位public

  -能夠含有構造函數,析構函數

  -不能含有引用類型的成員

  -不能繼承自其餘類,不能做爲基類

  -不能含有虛函數

  -匿名union在定義所在做用域能夠直接訪問union成員

  -匿名union不能包含protected成員或private成員

  -全局匿名聯合必須是靜態(static)的

#include<iostream>
using namespace std;
union uniontest {
    uniontest() :i(10) {};//拷貝構造 i爲10
    int i;
    double d;
};
static union {
    int i;
    double d;
};
int main() {
    uniontest u;
    union {//匿名聯合的定義
        int i;
        double d;
    };
    cout << u.i << endl;//輸出uniontest聯合的10
    ::i = 20;
    cout << ::i << endl;//輸出全局靜態匿名聯合的20
    i = 30;
    cout << i << endl;//輸出局部匿名聯合的30
    system("pause");
    return 0;
}

explicit(顯式)構造函數

explicit修飾的構造函數能夠用來防止隱式轉換

explicit使用

#include<iostream>
using namespace std;
class test1 {
public:
    test1(int n) {
        num = n;
    }
private:
    int num;
};
class test2 {
public:
    explicit test2(int n)
    {
        num = n;
    }
private:
    int num;
};

int main() {
    test1 t1 = 12;//隱式調用其構造函數,成功
    //test2 t2 = 12;//編譯錯誤,不能隱式調用其構造函數
    test2 t2(12);//顯式調用成功
    system("pause");
    return 0;
}

friend友元類和友元函數

能訪問私有成員

破壞封裝性

友元關係不可傳遞

友元關係的單向性

友元聲明的形式及數量不受限制

using

using聲明

一條using聲明語句一次只引入命名空間的一個成員,它使得咱們能夠清楚的知道程序中所引用的究竟是那個名字。如:using namespace_name::name;

構造函數的using聲明(c++11)

在c++11中,派生類可以重用其直接基類定義的構造函數

class derived:base{
public:
   using base::base;
   //.....  
};

如上using聲明,對於基類的每一個構造函數,編譯器都生成一個與之對應(形參列表徹底相同)的派生類構造函數。生成以下類型構造函數:

derived(parms):base(args){ }

儘可能少使用using指示 污染命名空間

通常來講,使用using明亮比使用using編譯命令更安全,這是因爲它只導入了制定的名稱。若是該名稱與局部名稱發生衝突,編譯器將發出指示。using編譯命令導入全部的名稱,包括可能並不須要的名稱。若是與局部名稱發生衝突,則局部名稱將覆蓋名稱空間版本,而編譯器不會發出警告。另外,名稱空間的開放性意味着名稱空間的名稱可能分散在多個地方,這使得難以準確的知道添加了那些名稱。

 

::範圍解析運算符

分類
1全局做用符(::name):用於類型名稱(類,類成員,成員函數,變量等),表示做用域爲全局命名空間
2類做用域符(class::name):用於表示制定類型的做用域範圍是具體某個類的
3命名空間做用域符(namespace::name):用於表示指定類型的做用域範圍是具體某個命名空間的
::使用
int count = 0;//全局(::)的count
class A{
public:
      static int count;//類A的count
};
int main(){
      ::count = 1;//設置全局的count的值爲1
      A::count = 2//設置類A的count爲2     
      int count = 0;    // 局部的 count
      count = 3;        // 設置局部的 count 的值爲 3
      return 0;
}

enum枚舉類型

限定做用域的枚舉類型

enum class open_modes{imput,output,append};

不限定做用域的枚舉類型

enum color{red ,yellow,green};

enum{floatPrec=6,doublePrec = 10};

decltype

decltype關鍵字用於檢查實體的聲明類型或表達式的類型及值分類。語法:

decltype(expression)

decltype使用

// 尾置返回容許咱們在參數列表以後聲明返回類型
template <typename It>
auto fcn(It beg, It end) -> decltype(*beg)
{
    // 處理序列
    return *beg;    // 返回序列中一個元素的引用
}
// 爲了使用模板參數成員,必須用 typename
template <typename It>
auto fcn2(It beg, It end) -> typename remove_reference<decltype(*beg)>::type
{
    // 處理序列
    return *beg;    // 返回序列中一個元素的拷貝
}

引用

左值引用

常規引用,通常表示對象的身份

右值引用

右值引用就是必須綁定到右值(一個臨時對象,將要銷燬的對象)的引用,通常表示對象的值。

右值引用可實現轉移語義(move sementics)和精確傳遞(perfect forwarding),它的主要目的有兩個方面:

  消除兩個對象交互時沒必要要的對象拷貝。節省運算存儲資源,提升效率

  可以更簡潔明確的定義泛型函數

引用摺疊

  • X& &X& &&X&& & 可摺疊成 X&
  • X&& && 可摺疊成 X&&

宏定義能夠實現相似於函數的功能,可是它始終不是函數,而宏定義中括弧中的參數也不是真的參數,在宏展開的時候參數進行的是一對一的替換。

成員初始化列表

好處

更高效:少了一次調用牧人構造函數的過程

有些場合必需要用初始化列表:

  常量成員,由於常量只能初始化不能賦值,因此必須放在初始化列表裏面

  引用類型,引用必須在定義的時候初始化,而且不能從新賦值,因此也要寫在初始化列表裏面

  沒有默認構造函數的類類型,由於使用初始化列表能夠沒必要調用默認構造函數來初始化,而是直接調用拷貝構造函數初始化。

initializer_list列表初始化 C++11

用花括號初始化器列表,列表初始化一個對象,其中對應構造函數接受一個std::initoalizer_list參數

#include<iostream>
#include<vector>
#include<initializer_list>
template<class T>
struct s {
    std::vector<T> v;
    s(std::initializer_list<T> l) :v(l) {
        std::cout << "constructed with a" << l.size() << "-element list\n";

    }
    void append(std::initializer_list<T> l) {
        v.insert(v.end(), l.begin(), l.end());
    }
    std::pair<const T*, std::size_t>c_arr() const {
        return{ &v[0],v.size() };//在return語句中複製列表初始化,這不適用std::initializer_list
    }
};
template<typename T>
void templated_fn(T) {}
int main() {
    s<int> s = { 1,3,2,4,5 };//複製初始化
    s.append({ 6,7,8 });//函數調用中的列表初始化
    std::cout << "size is now" << s.c_arr().second << " ints:\n";
    for (auto n : s.v)
        std::cout << n << ' ';
    std::cout << '\n';
    std::cout << "range-for over brace-init-list:\n";
    for (int x : {-1, -2, -3})
        std::cout << x << ' ';
    std::cout << std::endl;
    auto al = { 10,11,12 };
    std::cout<< "The list bound to auto has size() = " << al.size() << '\n';
    // templated_fn({1, 2, 3}); // 編譯錯誤!「 {1, 2, 3} 」不是表達式,
                             // 它無類型,故 T 沒法推導
    templated_fn<std::initializer_list<int>>({ 1,2,3 });//ok
    templated_fn<std::vector<int>>({ 1,2,3 });
    system("pause");
    return 0;
}
相關文章
相關標籤/搜索