常見問題(持續更新。。。)

1.使用類或者結構體時,再定義的後面要添加";"

class Flyingtime

{

        ......

};

struct Flyingtime

{

        .......

};

2.在windows下使用include包含相對路徑下的文件要使用"/"

例如在flyingtime下有如下文件夾:

header、source、information等

如果要在source文件夾下的test.cpp要用到header下的test.h文件

#include "../header/test.h"

3. 在類中聲明"static"類型變量,在類的定義中就不能再次使用"static"

 

//test.h #ifndef TEST_H #define TEST_H class test {      public:            test();            ~test();            static int const test_int() const;      private:            static int int_arg; }; #endif //test.cpp #include <iostream> using std::cout; using std::endl; #include "test.h" int test::int_arg=0; test::test() {     int_arg++;         cout << "test class start!" << endl; } test::~test() {     int_arg--;     cout << "test class end!" << endl; } int const test::test_int() const {     cout << "static argument is : " << int_arg << endl;     return int_arg; }

4.類沒有對象也可以訪問其靜態成員變量,但必須用靜態成員函數進行訪問

//承上代碼 #include <iostream> using std::cout; using std::endl; #include "test.h" void main() {      test *abc= new test();      abc->test_int();                                //此時可以用abc->int_arg來獲取靜態變量的值,因爲test類的對象abc存在      delete abc;      abc=0;      test::test_int();                                //此時必須用類的靜態成員函數來獲取靜態變量的值 }

5. 在一些信息安全較高的地方最好使用“代理類

假設一個類進行信息加密Encrypt,他的一個代理類EncryptPtr

encrypt.h

#ifndef ENCRYPT_H  #define ENCRYPT_H  #region  class Encrypt {    public:          Encrypt(FILE *,FILE *);          ~Encrypt();          void Encrypt_Code();    private:          int m_Encrypt;          //這裏有構造函數隨即生成  }; #endregion  #endif

encrypt.cpp

//encrypt.cpp #include <iostream> using std::cout; using std::endl; #include <ctime> #include <cstdlib> #include "encrypt.h" Encrypt::Encrypt(FILE *fpinput,FILE *fpoutput) {   assert(fpinput);        //以前沒用過,這裏也不知道是不是正確   assert(fpoutput);   srand(time(0));   m_Encrypt=rand();       //生成加密時用的密鑰 } Encrypt::~Encrypt() {     m_Encrypt=0; } void Encrypt::Encrypt_Code() {   //read fpinput->buffer   //buffer xor m_Encrypt   //buffer write->fpoutput }

encrypt_ptr.h

#ifndef ENCRYPT_PTR_H #define ENCRYPT_PTR_H class Encrypt;     //在這裏只需要聲明一次,而不需要包含它的頭文件 class Encrypt_Ptr {    public:       Encrypt_Ptr(FILE *,FILE *);       ~Encrypt_Ptr();    privat:       Encrypt_Ptr *cpEncrypt; }; #endif

encrypt_ptr.cpp

#include "encrypt.h"         //被代理類的頭文件 #include "encrypt_ptr.h"     //代理類的頭文件 Encrypt_Ptr::Encrypt_Ptr(FILE *fpinput,FILE *fpoutput)    :cpEncrypt(new Encrypt(fpinput,fpoutput)) {     cpEncrypt->Encrypt_Code(); } Encrypt_Ptr::~Encrypt_Ptr() {     delete cpEncrypt; }

7.靜態成員函數只能訪問靜態成員變量,將靜態成員函數聲明爲"const"是語法錯誤

因爲靜態成員函數是獨立於類而存在的,也就是說類並不給其靜態成員函數傳遞this指針,這就意味着靜態成員函數只能看到靜態成員變量,而類中的其他成員變量它是看不到的。成員函數的const實際上也是修飾的this指針,所以將靜態成員函數聲明爲"const"是語法錯誤!

8.可以重載的操作符有:

+    -    *    /    %    ^    &       |    ~    !    =    >    <    +=    -=    *=    /=    %=    ^=    &=    |=    <<    >>

>>=    <<=    ==    !=    <=    >=    &&    ||    ++    --    ->*    '    ->    []    ()    new    delete    new[]    delete[]

不能被重載的操作符有:

.    .*    ::    ?:    sizeof

9.什麼時候定義類成員操作符重載,什麼時候定義非類成員操作符重載
(1)如果一個重載操作符是類成員,那麼只有當跟它一起使用的左操作數是該類對象時,它纔會被調用,如果該操作符的左操作數必須是其他類型,那麼重載操作符必須是非類成員操作符重載。
(2)C++要求,賦值(=),下標([ ]),調用(())和成員訪問箭頭(->)操作符必須被指定爲類成員操作符,否則錯誤。

10.當做重載操作符操作時,如果類的實例對象出現在重載操作符右邊時,最好將該重載操作符的函數聲明爲友元函數

//array.h #ifndef ARRAY_H #define ARRAY_H #include <iostream> using::ostream; using::istream; class Array {   public:     Array(int=10);     Array(const Array &);     friend ostream &operator <<(ostream &,const Array &);   //重載 <<     friend istream &operator >>(istream &,Array &);         //重載 >>     Array operator =(const Array &);                       //重載 =     bool operator ==(const Array &) const;                  //重載 ==      bool operator !=(const Array &) const;                  //重載 !=     int operator [](int subscript);                        //重載 [] //  與上一個效果應該是一樣的 //  int operator [](int subscript);                              int GetSize();     ~Array();   private:     int m_size;     int *lp_array;     static int m_obj_counter;  }; #endif //array.cpp 這裏是array類的實現 #include <iostream> using std::cin; using std::cout; #include <iomanip> using std::setw; #include <cstdlib> #include <cassert> #include "array.h" Array::Array(int size) {     m_size=size>0 ? size : 10;    l p_array=new int[m_size];     assert(lp_array!=0); } Array::Array(Array &rf_array)     :m_size=rf_array.m_size { //  m_size=rf_array.m_size;      //與前面的一樣     lp_array=new int[m_size];     for(int i=0;i<m_size;i++)     {         lp_array[i]=rf_array.lp_array[i];     } } Array::~Array() {     delete[] lp_array;     m_size=0; } ostream &operator <<(ostream &output,const Array &rf_array) {     for(int i=0;i<rf_array.m_size;i++)     {        output << rf_array.p_array[i] << setw(10);        if(rf_array.m_size%5==0)         output << endl ;        }     if(rf_array.m_size%5!=0) output << endl;     return output; } istream &operator >>(istream &input,Array &rf_array) {     for(int i=0;i<rf_array.m_size;i++)     {        input >> rf_array.lp_array[i] ;     }     return input; } Array &Array::operator=(const Array &rf_array) {     if(&rf_array!=this)     {        if(m_size!=rf_array.m_size) m_size=rf_array.m_size;        delete[] lp_array;        lp_array=new int[m_size];        assert(lp_array!=0);        for(int i=0;i<m_size;i++)        {           lp_array[i]=rf_array.lp_array[i];        }       }     return *this; } int &Array::operator[](int subscript) {     assert(subscript>=0&&subscript<m_size);     return lp_array[subscript];          //在實際程序中驗證該返回值是正確的。。。 cout << arrayobject[intobject] or //arrayobject[intobject]=intobject 都是正確的 //在C++編程金典中有個例子它返回值類似於下面: //  return lp_array[subscript]; //我覺得既然是返回值爲"&"就不應該返回一個數組的值,而是當前數值下標的指針 //這裏我覺得有些疑問,如果有朋友知道該返回什麼請告訴我一聲,謝謝哈 } bool Array::operator ==(const Array &rf_array) const {     if(rf_array.m_size!=m_size) return false;     for(int i=0;i<m_size;i++)     {        if(lp_array[i]!=rf_array.lp_array[i]) return false;     }     return true; } bool Array::operator !=(const Array &rf_array) const {     if(rf_array.m_size!=m_size) return true;     for(int i=0;i<m_size;i++)     {        if(lp_array[i]!=rf_array.lp_array[i]) return true;     }     return false; } //main.cpp #include <iostream> using std::cout; using std::cin; #include "array.h" bool main() {    Array a(8),b(6); //因爲類實例對象出現在重載操作符的右邊,所以在定義重載操作的時候需要定義爲 //friend類型,編譯器遇到表達式cin >> arrayobject;會解析成 //operator >>(cin,arrayobject)如果定義爲類的成員函數時可能寫的時候 //就不是那麼好理解了(如果爲類的成員函數則需寫成arrayobject<<cout)    cin >> a ;    cin >> b ;    cout << a ;    cout << b;    if(a!=b) a=b;    cout << a;    cout << b;    if(a==b) return true;    return false; }

比較有疑問的那部分代碼的反彙編代碼如下:

return p_array[subscript];  
004130C9  mov         eax,dword ptr [this]
004130CC  mov         ecx,dword ptr [eax+4]
004130CF  mov         edx,dword ptr [subscript]
004130D2  lea         eax,[ecx+edx*4]

可以看出這裏 return p_array[subscript]就是返回它的指針

11.派生類的對象就是基類對象,但反過來不成立;如果想讓基類對象轉換成派生類對象必須進行顯式的轉換

 如果將上面的:

template<class T1>

 template<class T2>

寫成:

template <class T1,class T2>即爲錯誤,因爲它們兩個的作用域是完全不同的

//基類 class a {     public:         a();         ~a();     protected:         int x;         int y; }; a::a() :x(0),y(0) {} a::~a() {} //通過繼承基類a生成派生類b class b : public a {     public:         b();         ~b();     private:         int count; }; b::b() :a()        //這裏顯式初始化基類a {} b::~b() {} int main() {     a *cp_a=0,a_obj;     b *cp_b=0,b_obj;          cp_a=&a_obj;       //同類型賦值     cp_b=&b_obj;     cp_a=&b_ojb;       //派生類的對象就是基類的對象體現在這裏     cp_b=static_cast<b *>&a_obj;     //將基類對象轉換成派生類對象需要     cp_b=static_cast<b *>cp_a;       //顯式的轉換     //這裏需要注意使用強制轉換需要程序員自己對該強制轉換負責,即程序員要控制指針的使用     //當指針指向一個不存在成員變量時也許不會出錯,但是用指針指向一個不存在成員函數    //時一定會出錯     return 0; }

12.基類構造函數賦值操作符不能被派生類函數繼承,但派生類的構造函數和賦值操作符可以調用基類的構造函數和賦值操作符。

13.關於位段的一些問題:

//使用關鍵字struct來聲明位段  struct test {      unsigned a : 2 ;      unsigned b : 4 ;      unsigned c : 2 ; };

(1.)位段操作與機器有關,有些機器允許位段跨越字邊界,而有的不可以;

(2.)位段成員的類型只能是unsigned或者int

(3.)試圖取位段的地址或者試圖訪問位段中單獨的位都是錯誤的操作;

14.在迭代器中添加新的元素可能會使迭代器失效尤其存儲end返回的迭代器

因爲end返回的迭代器並非是該容器的最後一個迭代器,而是該容器的最後一個迭代器的下一個

#include <iostream> #include <vector> int main() {      std::vector<int> v;      std::vector<int>::iterator first=v.begin(),                                 last=v.end();//這裏導致last迭代器失效             while(first != last)       //因爲前面last失效,所以這裏也是不對的                                     //最終可能導致一個死循環      {           first = v.insert(first,66);           ++first;      }      }

15.應該保持成員初始化列表中成員列出的順序和它們在類中聲明順序相同因爲類成員是按照它們在類裏聲明的順序進行初始化的,和它們在成員列表中列出的順序沒有一點關係

例如:

#ifndef ARRAY_H #define ARRAY_H #include <cstdlib> #include <vector> template<typename T> class Array { public:    Array(int lowBound,int highBound); private:    vector<T> data;    size_t size;    int mLow;    int mHigh; }; template <typename T> Array<T>::Array(int lowBound,int highBound) :mLow(lowBound),mHigh(highBound),size(highBound-lowBound+1),data(size) //注意上面在成員初始化列表中列出的成員初始化順序並不代表真實的初始化順序 //真正的初始化話順序在類的聲明中決定了,之所以要讓成員初始化列表中的順序同 //類聲明的順序一致是想提醒用戶類的成員將按照什麼樣的順序進行初始化 {} #endif

這裏data的大小並不確定,因爲首先初始話的是vector<T> data(size),而此時的size並沒有被初始化。正確的例子如下:

#ifndef ARRAY_H  #define ARRAY_H  #include <cstdlib>  #include <vector>  template<typename T> class Array { public:    Array(int lowBound,int highBound); private:    int mLow;          //類成員的聲明順序被改變了    int mHigh;    size_t size;    vector<T> data;  }; template <typename T> Array<T>::Array(int lowBound,int highBound) :mLow(lowBound),mHigh(highBound),size(highBound-lowBound+1),data(size) //成員初始化列表與類聲明中的保持一致了 //最終data會被初始化成一個size大小的vector<T>類型的變量 {}  #endif

16.注意賦值運算以及拷貝構造函數,尤其是存在繼承的關係中

template<typename T> class Base { public:     Base(const Base& default)     :mData(default.mData)     {}     Base& operator = (Base& default)     //注意這裏的返回值類型,必須爲*this類型,這樣可以保持賦值操作的連續性     //如:Base<int> a = b = c = d = 5;    //同時返回值不能爲const類型    //即const Base& operator = (Bast<T>&)  Error!    //如果用戶這樣賦值Base<int> (a=b)=c=5;    //因爲(a = b)返回爲const類型,所以再用c給它賦值就會出現錯誤     {         if(this == & default)         //這裏檢查是否對自身賦值         return *this;         mData = default.mData;         return *this;     } private:     T mData; }; template <typename T> class Child : public Base<T> { public:     Child(const Child& default)     :Base(default),mSubData(default.mSubData)     //如果在該拷貝構造函數中不加入Bast(default)那麼基類中的     //數據成員就不能得到初始化     //上面成員列表的順序也是按照它們初始化的順序書寫的     //保持一個良好的編程習慣不容易啊!     {     Child& operator=(const Child& default)     {          if(this == &default)          //檢查是否是對自身賦值          return *this;          //Base<T>::operator=(default);          //上面被註釋的部分有的編譯器並不支持           static_cast<Base&>(*this) = default;          mSubData=default.mSubData;          return *this;     } private:     T mSubData; }

17.如果派生類私有繼承基類,並且在派生類中在調用基類中的函數可以用using顯式的聲明

#include <iostream> using std::cout; using std::endl; using std::cin; class a { public:     void showa()     {         cout << "a.show/n";     } }; class b : private a { public:     using a::showa;          //這裏只能用名稱,而不能用showa()     void showb()     {         cout << "b.show/n";         showa();     } }; void main() {     b name;     name.showb();     int i;     cin >> i;     return; }

18.請看上面的例子,基類a並沒有實現virtual ~a(),爲了讓以a爲基類的派生類不產生錯誤,派生類只能實現私有繼承。之所以這麼做有以下原因:

(1)不實現virtual ~a(),是爲了不再增加派生類的體積,如果實現virtual ~a(),將引入vtab。

(2)之所以在派生類中私有繼承基類,是爲了在派生類中不能直接調用基類的析構函數,如:

a *pa;

b *pb=new b;

pa=pb;    //加入基類的私有繼承這裏就會出現錯誤

delete pa; //這裏不加入virtual ~a()將會出現錯誤,我們把a私有繼承,那麼這裏就不會出現錯誤!因爲a的析構函數是私有的。

19.從一個類模板生成的每個類都將得到類模板中每個static成員的一個副本

摘自《C++程序設計(特別版)》,還沒有完全明白它的意思,有一個例子:

20.一個模板的模板參數表其模板成員的模板參數表不能組合在一起。

 

   

 

發佈了18 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章