文章目錄
- 1.new 在現有的堆上再次分配
- 2.一些喜歡出錯的運算子多載(括弧、使用者自訂)
- 3.const 修飾函數
- 4.採用const_cast 進行轉換
- 5.不可以拷貝的對象
- 6.RTTI(Run-Time Type Identification)
- 7.typeid
- 8.箭頭重載
- 9.等繼.....
1.new 在現有的堆上再次分配
char *cp = new char[100],ch[50];
int *p = new(cp) int ;
*p=58;
delete p;
cp = cp+sizeof(int);
delete cp;//錯誤,不能釋放兩次
int *p = new(ch) int ; //利用現有棧空間重新分配
delete *p ; //錯誤,不能釋放棧空間
2.一些喜歡出錯的運算子多載(括弧、使用者自訂)
#include <iostream>using namespace std;class test{ friend ostream & operator << (const ostream &o,const test &t); }; ostream& operator << (ostream &o,const test &t){ cout <<"test"<<endl; return o ; } class Time{ public:int operator()() { //小括弧重載 版本0 注意和下面使用者自訂轉換的區別 cout <<"bracket"<<endl; return h*3600 + m*60 + s; } operator test() { //使用者自訂轉換 1.使用者定義的轉換不能指定傳回型別 2.“Time::operator test”: 必須返回一個值cout <<"cast test"<<endl; static test t; //return t; //返回class test的一個引用 } void operator()(int h, int m, int s) { //小括弧重載 版本3 this->h = h; this->m = m; this->s = s; } private:int h;int m;int s ;} ;int main(int argc,char**argv){ Time t; t(1,1,1) ; //小括弧重載 ,並帶有三個參數 cout<<t()<<endl;//小括弧重載 版本0 cout<< test (t) ; return 0; }
1.使用者定義的轉換不能指定傳回型別
2.“Time::operator test”: 必須返回一個值
3.const 修飾函數
const char* const foo(char const * const str) const
第一個const表示傳回型別為const,也就是不能把此函數的傳回值當作左值來使用。
第二個const表示指標的不可變性,但在這是可以省略,因為返類型已經是const。
第三個cosnt表示str的常量性,也就其內容是不能改變,可以寫在其前面的char的前面。
第四個cosnt表示str的指標的常量性,也就是此指標不能指向別的地址。
第五個cosnt表示此函數的常量性(前提是類的成員函數),不能修改所在類的資料成員。
更詳細的用法,參考:http://blog.csdn.net/Eric_Jo/article/details/4138548
4.採用const_cast 進行轉換
用法:const_cast <type_id> (expression)
該運算子用來修改類型的const或volatile屬性。除了const 或volatile修飾之外, type_id和expression的類型是一樣的。
· 常量指標被轉化成非常量指標,並且仍然指向原來的對象;
· 常量引用被轉換成非常量引用,並且仍然指向原來的對象;
· 常量對象被轉換成非常量對象。
下面還是用代碼來說明問題
代碼一:
const int m = 15;const int *const_p = &m;int *p = const_cast<int*> (const_p) ;*p = 18;cout << *p<<*const_p<<m; ;
輸出結果: 181815
代碼二:
int m = 15;const int *const_p = &m;int *p = const_cast<int*> (const_p) ;*p = 18;cout << *p<<*const_p<<m; ;
輸出結果: 181818
代碼三:
const int m = 15; const int &mconst = m;int &mnc =const_cast<int&>(mconst ); //或 int &mnc =const_cast<int&>(m);mnc = 80;cout << mnc <<mconst <<m;
輸出結果:808015
這真是一件奇怪的事情,但是這是件好事:說明C++裡是const,就是const,外界千變萬變,我就不變。不然真的會亂套了,const也沒有存在的意義了。
IBM的C++指南稱呼“*modifier
= 7;”為“未定義行為(Undefined Behavior)”。所謂未定義,是說這個語句在標準C++中沒有明確的規定,由編譯器來決定如何處理。
位元運算的左移操作也可算一種未定義行為,因為我們不確定是邏輯左移,還是算數左移。
再比如下邊的語句:v[i] = i++; 也是一種未定義行為,因為我們不知道是先做自增,還是先用來找數組中的位置。
對於未定義行為,我們所能做的所要做的就是避免出現這樣的語句。對於const資料我們更要這樣保證:絕對不對const資料進行重新賦值。
更詳細的內容參考:http://www.cnblogs.com/ider/archive/2011/07/22/cpp_cast_operator_part2.html
5.不可以拷貝的對象
C++中不可拷貝的對象一般為IO對象,不可拷貝的原因:io對象操作各種不同的輸入輸出裝置的控制代碼,如果允許複製,會出現兩個不同的對象操作同一個裝置控制代碼,如果其中一個析構了,該裝置被關閉,但另一個對象仍使用該控制代碼,會導致不可預測的運行期錯誤,是危險的。因此io對象不允許複製。
不可複製的實現:
在C++中,類的拷貝主要是通過拷貝建構函式和賦值函數,再者就是為拷貝專門實現的成員方法。由於拷貝建構函式和賦值函數在使用者為提供的情況下是由C++編譯器自動產生的,而且是public成員,因此預設的C++類都有拷貝功能。因此,可以顯示的將建構函式和賦值函數設為私人成員,
在此留一下問題: 友員friend 是可以訪問私友成員,如果讓友員也不訪問部分私友成員呢???
6.RTTI(Run-Time Type Identification)
先看看下面一段代碼:
#include <iostream>using namespace std;class A{int m[15];public:virtual void prinA(){cout <<"this is A"<<endl;} };class B: public A{ //若換成虛繼承,會有什麼情況?public:virtual void prinB(){cout <<"this is B"<<endl;} };void fun_1(A* pa){B* pb = dynamic_cast<B*>(pa);if(pb==NULL){pa->prinA();}else{pb->prinB();}}void fun_2(A* pa){B*pb=NULL;if(typeid(*pa)== typeid(B) ){ pb= static_cast<B*>(pa) ;pb->prinB();}else{pa->prinA();}}int main(){//cout <<sizeof(A)<<sizeof(B);//6464A * pa =new B ;cout <<typeid(pa).name()<<endl;cout <<typeid(*pa).name()<<endl;fun_1(pa);fun_2(pa);A* paa = new A ;fun_1(paa);fun_2(paa);return 0;}
先不用看運行結果,可以先試著做做,看看與運行結果是否一樣!
運行結果:
class A *
class B
this is B
this is B
this is A
this is A
Press any key to continue . . .
至於細仔的說明,這裡就不弄出來了,給一個參考的連結吧!
更多細節參考:http://www.cnblogs.com/zhyg6516/archive/2011/03/07/1971898.html
7.typeid
在揭開typeid神秘面紗之前,我們先來瞭解一下RTTI(Run-Time
Type Identification,運行時類型識別),它使程式能夠擷取由基指標或引用所指向的對象的實際衍生類別型,即允許“用指向基類的指標或引用來操作對象”的程式能夠擷取到“這些指標或引用所指對象”的實際衍生類別型。在C++中,為了支援RTTI提供了兩個操作符:dynamic_cast和typeid。
dynamic_cast允許運行時刻進行類型轉換,從而使程式能夠在一個類階層中安全地轉化類型,與之相對應的還有一個非安全的轉換操作符static_cast,因為這不是本文的討論重點,所以這裡不再詳述,感興趣的可以自行查閱資料。下面就開始今天我們的話題:typeid。
typeid是C++的關鍵字之一,等同於sizeof這類的操作符。typeid操作符的返回結果是名為type_info的標準庫類型的常量對象的引用(在標頭檔typeinfo中定義,稍後我們看一下vs和gcc庫裡面的源碼),它的運算式有兩種形式。
如果運算式的類型是類類型且至少包含有一個虛函數,則typeid操作符返回運算式的動態類型,需要在運行時計算;否則,typeid操作符返回運算式的靜態類型,在編譯時間就可以計算。
ISO C++標準並沒有確切定義type_info,它的確切定義編譯器相關的,但是標準卻規定了其實現必需提供如下四種操作(在之後的章節中我會來分析type_info類檔案的源碼):
| t1 == t2 |
如果兩個對象t1和t2類型相同,則返回true;否則返回false |
| t1 != t2 |
如果兩個對象t1和t2類型不同,則返回true;否則返回false |
| t.name() |
傳回型別的C-style字串,類型名字用系統相關的方法產生 |
| t1.before(t2) |
返回指出t1是否出現在t2之前的bool值 |
type_info類提供了public虛 解構函式,以使使用者能夠用其作為基類。它的預設建構函式和拷貝建構函式及賦值操作符都定義為private,所以不能定義或複製type_info類型的對象。程式中建立type_info對象的唯一方法是使用typeid操作符(由此可見,如果把typeid看作函數的話,其應該是type_info的
友元)。type_info的name成員函數返回C-style的字串,用來表示相應的類型名,但務必注意這個返回的類型名與程式中使用的相應類型名並不一定一致(往往如此,見後面的程式),這具體由編譯器的實現所決定的,標準只要求實現為每個類型返回唯一的字串。
8.箭頭重載
1.重載箭頭操作符必須返回指向類類型的指標,2.或者返回定義了自己的箭頭操作符的類類型對象
#include <iostream>using namespace std;class A{public:void action(){cout << "Action in class A!" << endl;}};class B{A a;public:A* operator->(){return &a;}void action(){cout << "Action in class B!" << endl;}void printB(){cout << "I am B" <<endl;}};class C{B b;public:B operator->(){return b;}void action(){cout << "Action in class C!" << endl;}};int main(int argc, char *argv[]){B b;//b->printB();//error C2039: “printB”: 不是“A”的成員b->action() ;//正確,調用 A::action()C* pc = new C;pc->action();//正確,調用C::action();C c;c->action();//正確,調用C::operator-> ,再調用B::operator->,再調用A::operator->,最後調用A::action()getchar();return 0;}
9.等繼.....