国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

c/c++ 奇技淫巧(一些c語言的技巧)

2020-05-23 13:48:24
字體:
來源:轉載
供稿:網友

一. 變長數組

嚴格說來,變長數組的實現在c++中并不是一件麻煩的事情。Stl中的vector本身就是一個變長數組,并且有自動管理內存的能力。
但是在c中,實現變長數組就稍顯麻煩。用C實現,必然需要一個結構,結構當中應當有一個指針,指針分配一段內存空間,空間大小根據需要而定,而且必須有另外一個字段記錄究竟開辟了多大多長的空間。
大致描述如下:

Struct MutableLenArray{  Int count;  Char* p;};

P = new Char[Count];

沒什么問題,但是C語言的使用者有個最大的自豪就在于對于效率、空間使用的掌控。他們會有這樣的疑問,如果count=0,那么p就沒必要了,白白占了4(64位系統為8)個字節的空間,簡直浪費。
那有沒有更好的方式能實現上面的需求,又保證空間合理呢?答案是有的,用0長度

Struct MutableLenArray { Int count; Char p[0]; }; 

和上面的結構使用方法一致,但是我們可以用sizeof嘗試讀取其大小,發現竟然只有count字段的長度4字節,p沒有被分配空間。完美!

二. 宏的妙用

1. #和

“#”符號把一個符號直接轉換為字符串,例如:

#define TO_STRING(x) #x const char *str = TO_STRING( test ); 

str的內容就是”test “,也就是說#會把其后的符號 直接加上雙引號。
這個特性為c++反射的實現提供了極大便利,可以參考博主的下一篇文章,c++反射的簡單實現。

##符號會連接兩個符號,從而產生新的符號(詞法層次),例如:

#define SIGN( x ) INT_##x   int SIGN( 1 ); 

宏被展開后將成為:int INT_1;
可以把##看成連字符,連字符為則為新符號的產生提供了方便。Google的Gtest框架就巧妙的運用了連字符來生成新的測試案例。

2. 變參宏

#define LOG( format, ... ) printf( format, __VA_ARGS__ )   LOG( "%s %d", str, count ); 

VA_ARGS是系統預定義宏,被自動替換為參數列表。
經常需要進行輸出格式化,重定義時,可以用到以上技巧。

3. 宏參數的prescan

prescan的定義:當一個宏參數被放進宏體時,這個宏參數會首先被全部展開(有例外,見下文)。當展開后的宏參數被放進宏體時, 預處理器對新展開的宏體進行第二次掃描,并繼續展開。例如:

#define PARAM( x ) x   #define ADDPARAM( x ) INT_##x   PARAM( ADDPARAM( 1 ) ); 

因為ADDPARAM( 1 ) 是作為PARAM的宏參數,所以先將ADDPARAM( 1 )展開為INT_1,然后再將INT_1放進PARAM。
例外情況是,如果PARAM宏里對宏參數使用了#或##,那么宏參數不會被展開:

#define PARAM( x ) #x   #define ADDPARAM( x ) INT_##x 

PARAM( ADDPARAM( 1 ) ); 將被展開為”ADDPARAM( 1 )”。

所以此時要得到“INT_1”的結果,必須加入一個中間宏:

#define PARAM(x) PARAM1(x)#define PARAM1( x ) #x 

PARAM( ADDPARAM( 1 ) );此時的結果將會是“INT_1”。根據prescan原則,當ADDPARAM(1)傳入,會展開得到INT_1,然后將INT_1帶入PARAM1宏,最終得到“INT_1”的結果。

4. 接口宏

以下部分,摘自網上博客,僅作聲明。
C++的目標之一就是把類的聲明和定義分離開來,這對于項目的開發極其有利——這可以使開發人員不用看到類的實現就能知曉類的功能。但是,C++實現類的聲明與類定義的分離的方法會導致一些額外的工作——每個非內聯函數的表示都需要寫兩次,一次在類聲明中,一次在類定義中。
代碼如下:

// .h File class Element { void Tick (); };// .cpp File void Element ::Tick () { // todo }

由于Tick的標識在兩個地方都出現了,因此如果我們需要改變這個方法的參數的時候(改變函數名、返回類型或者加const),我們需要改變兩個地方。
當然通常這沒有什么工作量,但是有些情況下這個特性會帶來不少麻煩。
舉個例子,如果我們有一個叫做BaseClass的基類,有三個從BaseClass繼承而來的子類——D1、D2和D3.其中BaseClass聲明了一個虛函數Foo()并且有一個缺省實現,并且D1、D2、D3中重載了Foo()函數。
現在,如果說我們給BaseClass::Foo()添加一個參數,但是忘了給D3中做相應的修改。麻煩來了——編譯可以通過,編譯器會把BaseClass::Foo(…)和D3::Foo()當成兩個完全不同的函數。當我們想通過虛函數機制來調用D3的Foo的時候,這就容易出一些問題。
UE4中光繼承自AActor類的類就有上千個,如果需要對AActor類做一個修改,那么如果使用傳統方法,我們還要針對上千個派生類進行修改,而且萬一有一個派生類沒有修改,編譯器也不會報錯!
這么看來,理想的情況是我們希望一個函數的表示只在一個地方存在,如果說只聲明BaseClass::Foo()一次,然后再它的派生類中不用再額外聲明Foo就好了。
而且在效率方面來說,在C++中使用繼承的時候我們經常會使用很多淺層次的類繼承關系,一個父類往往有一堆子類。很多時候我們只需要把很多互不相關的功能集成到一個單獨的類繼承家族里面。
對于淺繼承來說,我們只是把開始的父類聲明為一個接口——也就是說它聲明了一些虛函數(大部分是純虛函數)。在大多數情況下,我們會在這個類家族里面有一個基類以及其余的派生類。
如果說我們的基類有10個函數,我們從這個基類派生了20個類,那么我們就需要額外做200個函數聲明。但是這些聲明的目的往往只是為了Implement基類中的那些方法而已,這就或多或少的容易使得頭文件不好維護。
傳統方法的實現
如果說我們有一個Animal的類,這個類被視為基類,我們希望從這個基類派生出不同的子類。在Animal中有3個純需函數,如下所示:

class Animal { public: virtual std :: string GetName () const = 0 ; virtual Vector3f GetPosition () const = 0; virtual Vector3f GetVelocity () const = 0; }; 

同時,這個基類擁有三個派生類——Monkey,Tiger,Lion。
那么我們三個方法的每一個都會在7個地方存在:Animal中一次,Monkey、Lion、Tiget的聲明和定義各一次。
然后假設我們做一個小改動——我們想將GetPosition和GetVelocity的返回類型改為Vector4f以適應Transform變換,那么我們就要在7個地方進行修改:Animal的.h文件,Lion、Tiger和Monkey的.h文件和.cpp文件。
使用宏的實現
有一種很妙的處理方法就是將這些方法進行包裝,改成所謂接口宏的形式。我們可以試試看:

#define INTERFACE_ANIMAL(terminal)             /public:                           /  virtual std::string GetName() const ##terminal     /  virtual IntVector GetPosition() const ##terminal    /  virtual IntVector GetVelocity() const ##terminal    #define BASE_ANIMAL   INTERFACE_ANIMAL(=0;)#define DERIVED_ANIMAL INTERFACE_ANIMAL(;)

值得一提的是,##符號代表的是連接,/符號代表的是把下一行的連起來。
通過這些宏,我們就可以大大簡化Animal的聲明,還有所有從它派生的類的聲明了:

// Animal.hclass Animal{  BASE_ANIMAL ;};// Monkey.hclass Monkey : public Animal{  DERIVED_ANIMAL ;};// Lion.hclass Lion : public Animal{  DERIVED_ANIMAL ;};// Tiger.hclass Tiger : public Animal{  DERIVED_ANIMAL ;};

 

 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 水富县| 西乡县| 老河口市| 南宫市| 康马县| 黑龙江省| 和田市| 正阳县| 巩义市| 鸡泽县| 土默特右旗| 卢氏县| 沙洋县| 开封县| 延庆县| 阿拉善左旗| 龙口市| 峨山| 砚山县| 喀喇沁旗| 谢通门县| 遂溪县| 环江| 新干县| 罗江县| 乐陵市| 习水县| 鹿泉市| 巢湖市| 修水县| 溧水县| 龙游县| 浙江省| 通化县| 香港| 湖南省| 扎囊县| 延庆县| 读书| 汾西县| 南康市|