日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
C++語言的15個(gè)晦澀特性

這個(gè)列表收集了 C++ 語言的一些晦澀(Obscure)特性,是我經(jīng)年累月研究這門語言的各個(gè)方面收集起來的。C++非常龐大,我總是能學(xué)到一些新知識(shí)。即使你對(duì)C++已了如指掌,也希望你能從列表中學(xué)到一些東西。下面列舉的特性,根據(jù)晦澀程度由淺入深進(jìn)行排序。

創(chuàng)新互聯(lián)公司是一家專注于成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)與策劃設(shè)計(jì),岫巖網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:岫巖等地區(qū)。岫巖做網(wǎng)站價(jià)格咨詢:028-86922220

  • 1. 方括號(hào)的真正含義
  • 2. 最煩人的解析
  • 3.替代運(yùn)算標(biāo)記符
  • 4. 重定義關(guān)鍵字
  • 5. Placement new
  • 6.在聲明變量的同時(shí)進(jìn)行分支
  • 7.成員函數(shù)的引用修飾符
  • 8.轉(zhuǎn)向完整的模板元編程
  • 9.指向成員的指針操作符
  • 10. 靜態(tài)實(shí)例方法
  • 11.重載++和–
  • 12.操作符重載和檢查順序
  • 13.函數(shù)作為模板參數(shù)
  • 14.模板的參數(shù)也是模板
  • 15.try塊作為函數(shù)

方括號(hào)的真正含義

用來訪問數(shù)組元素的ptr[3]其實(shí)只是*(ptr + 3)的縮寫,與用*(3 + ptr)是等價(jià)的,因此反過來與3[ptr]也是等價(jià)的,使用3[ptr]是完全有效的代碼

最煩人的解析

“most vexing parse”這個(gè)詞是由Scott Meyers提出來的,因?yàn)镃++語法聲明的二義性會(huì)導(dǎo)致有悖常理的行為:

 
 
 
  1. // 這個(gè)解釋正確?
  2. // 1) 類型std::string的變量會(huì)通過std::string()實(shí)例化嗎?
  3. // 2) 一個(gè)函數(shù)聲明,返回一個(gè)std::string值并有一個(gè)函數(shù)指針參數(shù),
  4. // 該函數(shù)也返回一個(gè)std::string但沒有參數(shù)?
  5. std::string foo(std::string());
  6.  
  7. // 還是這個(gè)正確?
  8. // 1)類型int變量會(huì)通過int(x)實(shí)例化嗎?
  9. // 2)一個(gè)函數(shù)聲明,返回一個(gè)int值并有一個(gè)參數(shù),
  10. // 該參數(shù)是一個(gè)名為x的int型變量嗎?
  11. int bar(int(x));

兩種情形下C++標(biāo)準(zhǔn)要求的是第二種解釋,即使***種解釋看起來更直觀。程序員可以通過包圍括號(hào)中變量的初始值來消除歧義:

 
 
 
  1. //加括號(hào)消除歧義
  2. std::string foo((std::string()));
  3. int bar((int(x)));

第二種情形讓人產(chǎn)生二義性的原因是int y = 3;等價(jià)于int(y) = 3;

譯者注:這一點(diǎn)我覺得有點(diǎn)迷惑,下面是我在g++下的測試用例:

 
 
 
  1. #include 
  2. #include 
  3. using namespace std;
  4.  
  5. int bar(int(x));   // 等價(jià)于int bar(int x)
  6.  
  7. string foo(string());  // 等價(jià)于string foo(string (*)())
  8.  
  9. string test() {
  10.     return "test";
  11. }
  12.  
  13. int main()
  14. {
  15.     cout << bar(2) << endl; // 輸出2
  16.     cout << foo(test); // 輸出test
  17.     return 0;
  18. }
  19.  
  20. int bar(int(x)) { 
  21.     return x;
  22. }
  23.  
  24. string foo(string (*fun)()) {
  25.     return (*fun)();
  26. }

能正確輸出,但如果按作者意思添加上括號(hào)后再編譯就會(huì)報(bào)一堆錯(cuò)誤:“在此作用域尚未聲明”、“重定義”等,還不清楚作者的意圖。

替代運(yùn)算標(biāo)記符

標(biāo)記符and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq, <%, %>, <: 和 :>都可以用來代替我們常用的&&, &=, &, |, ~, !, !=, ||, |=, ^, ^=, {, }, [ 和 ]。在鍵盤上缺乏必要的符號(hào)時(shí)你可以使用這些運(yùn)算標(biāo)記符來代替。

重定義關(guān)鍵字

通過預(yù)處理器重定義關(guān)鍵字從技術(shù)上講會(huì)引起錯(cuò)誤,但實(shí)際上是允許這樣做的。因此你可以使用類似#define true false 或 #define else來搞點(diǎn)惡作劇。但是,也有它合法有用的時(shí)候,例如,如果你正在使用一個(gè)很大的庫而且需要繞過C++訪問保護(hù)機(jī)制,除了給庫打補(bǔ)丁的方法外,你也可 以在包含該庫頭文件之前關(guān)閉訪問保護(hù)來解決,但要記得在包含庫頭文件之后一定要打開保護(hù)機(jī)制!

 
 
 
  1. #define class struct
  2. #define private public
  3. #define protected public
  4.  
  5. #include "library.h"
  6.  
  7. #undef class
  8. #undef private
  9. #undef protected

注意這種方式不是每一次都有效,跟你的編譯器有關(guān)。當(dāng)實(shí)例變量沒有被訪問控制符修飾時(shí),C++只需要將這些實(shí)例變量順序布局即可,所以編譯器可以對(duì) 訪問控制符組重新排序來自由更改內(nèi)存布局。例如,允許編譯器移動(dòng)所有的私有成員放到公有成員的后面。另一個(gè)潛在的問題是名稱重整(name mangling),Microsoft的C++編譯器將訪問控制符合并到它們的name mangling表里,因此改變訪問控制符意味著將破壞現(xiàn)有編譯代碼的兼容性。

譯者注:在C++中,Name Mangling 是為了支持重載而加入的一項(xiàng)技術(shù)。編譯器將目標(biāo)源文件中的名字進(jìn)行調(diào)整,這樣在目標(biāo)文件符號(hào)表中和連接過程中使用的名字和編譯目標(biāo)文件的源程序中的名字不一樣,從而實(shí)現(xiàn)重載。

#p#

Placement new

Placement new是new操作符的一個(gè)替代語法,作用在已分配的對(duì)象上,該對(duì)象已有正確的大小和正確的賦值,這包括建立虛函數(shù)表和調(diào)用構(gòu)造函數(shù)。

譯者注:placement new就是在用戶指定的內(nèi)存位置上構(gòu)建新的對(duì)象,這個(gè)構(gòu)建過程不需要額外分配內(nèi)存,只需要調(diào)用對(duì)象的構(gòu)造函數(shù)即可。placement new實(shí)際上是把原本new做的兩步工作分開來:***步自己分配內(nèi)存,第二步調(diào)用類的構(gòu)造函數(shù)在自己已分配的內(nèi)存上構(gòu)建新的對(duì)象。placement new的好處:1)在已分配好的內(nèi)存上進(jìn)行對(duì)象的構(gòu)建,構(gòu)建速度快。2)已分配好的內(nèi)存可以反復(fù)利用,有效的避免內(nèi)存碎片問題。

 
 
 
  1. #include 
  2. using namespace std;
  3.  
  4. struct Test {
  5.   int data;
  6.   Test() { cout << "Test::Test()" << endl; }
  7.   ~Test() { cout << "Test::~Test()" << endl; }
  8. };
  9.  
  10. int main() {
  11.   // Must allocate our own memory
  12.   Test *ptr = (Test *)malloc(sizeof(Test));
  13.  
  14.   // Use placement new
  15.   new (ptr) Test;
  16.  
  17.   // Must call the destructor ourselves
  18.   ptr->~Test();
  19.  
  20.   // Must release the memory ourselves
  21.   free(ptr);
  22.  
  23.   return 0;
  24. }

當(dāng)在性能關(guān)鍵的場合需要自定義分配器時(shí)可以使用Placement new。例如,一個(gè)slab分配器從單個(gè)的大內(nèi)存塊開始,使用placement new在塊里順序分配對(duì)象。這不僅避免了內(nèi)存碎片,也節(jié)省了malloc引起的堆遍歷的開銷。

在聲明變量的同時(shí)進(jìn)行分支

C++包含一個(gè)語法縮寫,能在聲明變量的同時(shí)進(jìn)行分支??雌饋砑认駟蝹€(gè)的變量聲明也可以有if或while這樣的分支條件。

 
 
 
  1. struct Event { virtual ~Event() {} };
  2. struct MouseEvent : Event { int x, y; };
  3. struct KeyboardEvent : Event { int key; };
  4.  
  5. void log(Event *event) {
  6.   if (MouseEvent *mouse = dynamic_cast(event))
  7.     std::cout << "MouseEvent " << mouse->x << " " << mouse->y << std::endl;
  8.  
  9.   else if (KeyboardEvent *keyboard = dynamic_cast(event))
  10.     std::cout << "KeyboardEvent " << keyboard->key << std::endl;
  11.  
  12.   else
  13.     std::cout << "Event" << std::endl;
  14. }

成員函數(shù)的引用修飾符

C++11允許成員函數(shù)在對(duì)象的值類型上進(jìn)行重載,this指針會(huì)將該對(duì)象作為一個(gè)引用修飾符。引用修飾符會(huì)放在cv限定詞(譯者注:CV限定詞有 三種:const限定符、volatile限定符和const-volatile限定符)相同的位置并依據(jù)this對(duì)象是左值還是右值影響重載解析:

 
 
 
  1. #include 
  2.  
  3. struct Foo {
  4.   void foo() & { std::cout << "lvalue" << std::endl; }
  5.   void foo() && { std::cout << "rvalue" << std::endl; }
  6. };
  7.  
  8. int main() {
  9.   Foo foo;
  10.   foo.foo(); // Prints "lvalue"
  11.   Foo().foo(); // Prints "rvalue"
  12.   return 0;
  13. }

轉(zhuǎn)向完整的模板元編程

C++模板是為了實(shí)現(xiàn)編譯時(shí)元編程,也就是該程序能生成其它的程序。設(shè)計(jì)模板系統(tǒng)的初衷是進(jìn)行簡單的類型替換,但是在C++標(biāo)準(zhǔn)化過程中突然發(fā)現(xiàn)模板實(shí)際上功能十分強(qiáng)大,足以執(zhí)行任意計(jì)算,雖然很笨拙很低效,但通過模板特化的確可以完成一些計(jì)算:

 
 
 
  1. // Recursive template for general case
  2. template 
  3. struct factorial {
  4.   enum { value = N * factorial::value };
  5. };
  6.  
  7. // Template specialization for base case
  8. template <>
  9. struct factorial<0> {
  10.   enum { value = 1 };
  11. };
  12.  
  13. enum { result = factorial<5>::value }; // 5 * 4 * 3 * 2 * 1 == 120

C++模板可以被認(rèn)為是一種功能型編程語言,因?yàn)樗鼈兪褂眠f歸而非迭代而且包含不可變狀態(tài)。你可以使用typedef創(chuàng)建一個(gè)任意類型的變量,使用enum創(chuàng)建一個(gè)int型變量,數(shù)據(jù)結(jié)構(gòu)內(nèi)嵌在類型自身。

 
 
 
  1. // Compile-time list of integers
  2. template 
  3. struct node {
  4.   enum { data = D };
  5.   typedef N next;
  6. };
  7. struct end {};
  8.  
  9. // Compile-time sum function
  10. template 
  11. struct sum {
  12.   enum { value = L::data + sum::value };
  13. };
  14. template <>
  15. struct sum {
  16.   enum { value = 0 };
  17. };
  18.  
  19. // Data structures are embedded in types
  20. typedef node<1, node<2, node<3, end> > > list123;
  21. enum { total = sum::value }; // 1 + 2 + 3 == 6

當(dāng)然這些例子沒什么用,但模板元編程的確可以做一些有用的事情,比如可以操作類型列表。但是,使用C++模板的編程語言可用性極低,因此請(qǐng)謹(jǐn)慎和少量使用。模板代碼很難閱讀,編譯速度慢,而且因其冗長和迷惑的錯(cuò)誤信息而難以調(diào)試。

#p#

指向成員的指針操作符

指向成員的指針操作符可以讓你在一個(gè)類的任何實(shí)例上描述指向某個(gè)成員的指針。有兩種pointer-to-member操作符,取值操作符*和指針操作符->:

 
 
 
  1. #include 
  2. using namespace std;
  3.  
  4. struct Test {
  5.   int num;
  6.   void func() {}
  7. };
  8.  
  9. // Notice the extra "Test::" in the pointer type
  10. int Test::*ptr_num = &Test::num;
  11. void (Test::*ptr_func)() = &Test::func;
  12.  
  13. int main() {
  14.   Test t;
  15.   Test *pt = new Test;
  16.  
  17.   // Call the stored member function
  18.   (t.*ptr_func)();
  19.   (pt->*ptr_func)();
  20.  
  21.   // Set the variable in the stored member slot
  22.   t.*ptr_num = 1;
  23.   pt->*ptr_num = 2;
  24.  
  25.   delete pt;
  26.   return 0;
  27. }

該特征實(shí)際上十分有用,尤其在寫庫的時(shí)候。例如,Boost::Python, 一個(gè)用來將C++綁定到Python對(duì)象的庫,就使用成員指針操作符,在包裝對(duì)象時(shí)很容易的指向成員。

 
 
 
  1. #include 
  2. #include 
  3. using namespace boost::python;
  4.  
  5. struct World {
  6.   std::string msg;
  7.   void greet() { std::cout << msg << std::endl; }
  8. };
  9.  
  10. BOOST_PYTHON_MODULE(hello) {
  11.   class_("World")
  12.     .def_readwrite("msg", &World::msg)
  13.     .def("greet", &World::greet);
  14. }

記住使用成員函數(shù)指針與普通函數(shù)指針是不同的。在成員函數(shù)指針和普通函數(shù)指針之間casting是無效的。例如,Microsoft編譯器里的成員 函數(shù)使用了一個(gè)稱為thiscall的優(yōu)化調(diào)用約定,thiscall將this參數(shù)放到ecx寄存器里,而普通函數(shù)的調(diào)用約定卻是在棧上解析所有的參 數(shù)。

而且,成員函數(shù)指針可能比普通指針大四倍左右,編譯器需要存儲(chǔ)函數(shù)體的地址,到正確父地址(多個(gè)繼承)的偏移,虛函數(shù)表(虛繼承)中另一個(gè)偏移的索引,甚至在對(duì)象自身內(nèi)部的虛函數(shù)表的偏移也需要存儲(chǔ)(為了前向聲明類型)。

 
 
 
  1. #include 
  2.  
  3. struct A {};
  4. struct B : virtual A {};
  5. struct C {};
  6. struct D : A, C {};
  7. struct E;
  8.  
  9. int main() {
  10.   std::cout << sizeof(void (A::*)()) << std::endl;
  11.   std::cout << sizeof(void (B::*)()) << std::endl;
  12.   std::cout << sizeof(void (D::*)()) << std::endl;
  13.   std::cout << sizeof(void (E::*)()) << std::endl;
  14.   return 0;
  15. }
  16.  
  17. // 32-bit Visual C++ 2008:  A = 4, B = 8, D = 12, E = 16
  18. // 32-bit GCC 4.2.1:        A = 8, B = 8, D = 8,  E = 8
  19. // 32-bit Digital Mars C++: A = 4, B = 4, D = 4,  E = 4

在Digital Mars編譯器里所有的成員函數(shù)都是相同的大小,這是源于這樣一個(gè)聰明的設(shè)計(jì):生成“thunk”函數(shù)來運(yùn)用右偏移而不是存儲(chǔ)指針自身內(nèi)部的偏移。

靜態(tài)實(shí)例方法

C++中可以通過實(shí)例調(diào)用靜態(tài)方法也可以通過類直接調(diào)用。這可以使你不需要更新任何調(diào)用點(diǎn)就可以將實(shí)例方法修改為靜態(tài)方法。

 
 
 
  1. struct Foo {
  2.   static void foo() {}
  3. };
  4.  
  5. // These are equivalent
  6. Foo::foo();
  7. Foo().foo();

重載++和–

C++的設(shè)計(jì)中自定義操作符的函數(shù)名稱就是操作符本身,這在大部分情況下都工作的很好。例如,一元操作符的-和二元操作符的-(取反和相減)可以通 過參數(shù)個(gè)數(shù)來區(qū)分。但這對(duì)于一元遞增和遞減操作符卻不奏效,因?yàn)樗鼈兊奶卣魉坪跬耆嗤?。C++語言有一個(gè)很笨拙的技巧來解決這個(gè)問題:后綴++和–操作 符必須有一個(gè)空的int參數(shù)作為標(biāo)記讓編譯器知道要進(jìn)行后綴操作(是的,只有int類型有效)。

 
 
 
  1. struct Number {
  2.   Number &operator ++ (); // Generate a prefix ++ operator
  3.   Number operator ++ (int); // Generate a postfix ++ operator
  4. };

操作符重載和檢查順序

重載,(逗號(hào)),||或者&&操作符會(huì)引起混亂,因?yàn)樗蚱屏苏5臋z查規(guī)則。通常情況下,逗號(hào)操作符在整個(gè)左邊檢查完畢才開始檢 查右邊,|| 和 &&操作符有短路行為:僅在必要時(shí)才會(huì)去檢查右邊。無論如何,操作符的重載版本僅僅是函數(shù)調(diào)用且函數(shù)調(diào)用以未指定的順序檢查它們的參數(shù)。

重載這些操作符只是一種濫用C++語法的方式。作為一個(gè)實(shí)例,下面我給出一個(gè)Python形式的無括號(hào)版打印語句的C++實(shí)現(xiàn):

 
 
 
  1. #include 
  2.  
  3. namespace __hidden__ {
  4.   struct print {
  5.     bool space;
  6.     print() : space(false) {}
  7.     ~print() { std::cout << std::endl; }
  8.  
  9.     template 
  10.     print &operator , (const T &t) {
  11.       if (space) std::cout << ' ';
  12.       else space = true;
  13.       std::cout << t;
  14.       return *this;
  15.     }
  16.   };
  17. }
  18.  
  19. #define print __hidden__::print(),
  20.  
  21. int main() {
  22.   int a = 1, b = 2;
  23.   print "this is a test";
  24.   print "the sum of", a, "and", b, "is", a + b;
  25.   return 0;
  26. }

#p#

函數(shù)作為模板參數(shù)

眾所周知,模板參數(shù)可以是特定的整數(shù)也可以是特定的函數(shù)。這使得編譯器在實(shí)例化模板代碼時(shí)內(nèi)聯(lián)調(diào)用特定的函數(shù)以獲得更高效的執(zhí)行。下面的例子里,函數(shù)memoize的模板參數(shù)也是一個(gè)函數(shù)且只有新的參數(shù)值才通過函數(shù)調(diào)用(舊的參數(shù)值可以通過cache獲得):

 
 
 
  1. #include 
  2.  
  3. template 
  4. int memoize(int x) {
  5.   static std::map cache;
  6.   std::map::iterator y = cache.find(x);
  7.   if (y != cache.end()) return y->second;
  8.   return cache[x] = f(x);
  9. }
  10.  
  11. int fib(int n) {
  12.   if (n < 2) return n;
  13.   return memoize(n - 1) + memoize(n - 2);
  14. }

模板的參數(shù)也是模板

模板參數(shù)實(shí)際上自身的參數(shù)也可以是模板,這可以讓你在實(shí)例化一個(gè)模板時(shí)可以不用模板參數(shù)就能夠傳遞模板類型??聪旅娴拇a:

 
 
 
  1. template 
  2. struct Cache { ... };
  3.  
  4. template 
  5. struct NetworkStore { ... };
  6.  
  7. template 
  8. struct MemoryStore { ... };
  9.  
  10. template 
  11. struct CachedStore {
  12.   Store store;
  13.   Cache cache;
  14. };
  15.  
  16. CachedStore, int> a;
  17. CachedStore, int> b;

CachedStore的cache存儲(chǔ)的數(shù)據(jù)類型與store的類型相同。然而我們在實(shí)例化一個(gè)CachedStore必須重復(fù)寫數(shù)據(jù)類型(上面的代碼 是int型),store本身要寫,CachedStore也要寫,關(guān)鍵是我們這并不能保證兩者的數(shù)據(jù)類型是一致的。我們真的只想要確定數(shù)據(jù)類型一次即 可,所以我們可以強(qiáng)制其不變,但是沒有類型參數(shù)的列表會(huì)引起編譯出錯(cuò):

 
 
 
  1. // 下面編譯通不過,因?yàn)镹etworkStore和MemoryStore缺失類型參數(shù)
  2. CachedStore c;
  3. CachedStore d;

模板的模板參數(shù)可以讓我們獲得想要的語法。注意你必須使用class關(guān)鍵字作為模板參數(shù)(他們自身的參數(shù)也是模板)

 
 
 
  1. template  class Store, typename T>
  2. struct CachedStore2 {
  3.   Store store;
  4.   Cache cache;
  5. };
  6.  
  7. CachedStore2 e;
  8. CachedStore2 f;

try塊作為函數(shù)

函數(shù)的try塊會(huì)在檢查構(gòu)造函數(shù)的初始化列表時(shí)捕獲拋出的異常。你不能在初始化列表的周圍加上try-catch塊,因?yàn)槠渲荒艹霈F(xiàn)在函數(shù)體外。為了解決這個(gè)問題,C++允許try-catch塊也可作為函數(shù)體:

 
 
 
  1. int f() { throw 0; }
  2.  
  3. // 這里沒有辦法捕獲由f()拋出的異常
  4. struct A {
  5.   int a;
  6.   A::A() : a(f()) {}
  7. };
  8.  
  9. // 如果try-catch塊被用作函數(shù)體并且初始化列表移至try關(guān)鍵字之后的話,
  10. // 那么由f()拋出的異常就可以捕獲到
  11. struct B {
  12.   int b;
  13.   B::B() try : b(f()) {
  14.   } catch(int e) {
  15.   }
  16. };

奇怪的是,這種語法不僅僅局限于構(gòu)造函數(shù),也可用于其他的所有函數(shù)定義。


文章名稱:C++語言的15個(gè)晦澀特性
瀏覽路徑:http://www.5511xx.com/article/dhhddge.html