2010-09-01 71 views
1

我很難以應用程序的方式編寫我的代碼。這是我的默認構造函數:關於編寫代碼的C++建議

Address::Address() : m_city(NULL), m_street(NULL), m_buildingNumber(0), m_apartmentNumber(0) 
{} 

...這是我的其他構造:

Address::Address(const char* city, const char* street, const int buildingNumber,const int apartmentNumber) : m_city(NULL), m_street(NULL) 
{ 
    SetAddress(city,street,buildingNumber,apartmentNumber); 
} 

我必須初始化我的城市和街道領域,因爲它們含有char *和我的二傳手使用刪除設置以新城市爲例。我非常希望聽到您的意見,就如何以正確的方式編寫代碼而不必重複代碼。 這是我SetAddress代碼:

bool Address::SetAddress(const char* city, const char* street, const int buildingNumber, const int apartmentNumber) 
{ 
    if (SetCity(city) == false || SetStreet(street) == false || SetBuildingNumber(buildingNumber) == false || SetApartmentNumber(apartmentNumber) == false) 
     return false; 
    return true; 
} 

,這是我SetCity:

bool Address::SetCity(const char* city) 
{ 
    if(city == NULL) 
     return false; 
    delete[] m_city; 
    m_city = new char[strlen(city)+1]; 
    strcpy(m_city, city); 
    return true; 
} 

1更多的問題,如果我這樣做改變的char *字符串如何檢查是否字符串城市犯規等於爲NULL我知道字符串沒有「==」運算符和字符串是一個對象,不能等於空, 我怎麼能檢查我得到的字符串是否確實是legeal。

+4

爲什麼const char *而不是const std :: string的引用? – 2010-09-01 19:45:29

+1

您正在對您的代碼進行微操作,而不是對其進行管理:您正在編寫太多的底層代碼來處理'char *',其中正常的C++ STL對象會比您希望處理的更好地處理它的大小...... – paercebal 2010-09-01 20:32:36

+0

從資源管理中分離資源使用情況。 – GManNickG 2010-09-01 20:48:25

回答

3

您可以結合兩個構造函數:

Address::Address(const char* city=NULL, 
       const char* street=NULL, 
       int buildingNumber=0, 
       int apartmentNumber=0) 
: m_city(city), 
    m_street(street), 
    m_buildingNumber(buildingNumber), 
    m_apartmentNumber(apartmentNumber) 
{} 

[上buildingNumberapartmentNumber頂級常量一事無成,並嘗試移動實施信息入接口,所以我將其刪除]

的,如果你真的喜歡:

Address::Address(const char* city=NULL, 
       const char* street=NULL, 
       int buildingNumber=0, 
       int apartmentNumber=0) 
{ 
    SetAddress(city,street,buildingNumber,apartmentNumber); 
} 

我一般喜歡前者,但如果SetAddress資格在其這可能是值得的。當然,建議使用std::string而不是指向char的指針也是一個很好的建議,但這或多或少是一個單獨的主題。

另一個小問題:這與您的原始代碼在一個基本方面有所不同。你的代碼需要0或4個參數給ctor。這將接受0到4之間的任何地方,因此人可能指定(例如)城市和街道,但不指定建築物號碼或公寓號碼。如果對你來說使用1,2或3個參數的嘗試被拒絕是非常重要的,那麼這種方法對你沒有用處。在這種情況下,額外的靈活性對我來說看起來有所改進 - 例如,如果有人住在單戶住宅中,省略公寓號碼是相當合理的。

15

您應該使用std::string而不是C字符串(const char*)。那麼你不必擔心有「刪除」功能,因爲std::string會爲你管理內存。

+1

.....和這個。 – 2010-09-01 19:45:26

+0

+1,因爲這是編碼它的明智方式...... – paercebal 2010-09-01 20:05:42

+0

請參閱我的答案以查看使用C風格字符串時的醜陋方式。 – 2010-09-01 21:13:37

4

我看到的唯一重複代碼是初始化器。既然你應該都使用初始化器,並且不能共享初始化器,那麼這裏需要一些代碼冗餘。我不會爲此擔心。

當新的C++出來時,你可以在初始化過程中調用前面的構造函數。在那之前,你只需要忍受這種小小的氣味。

+0

thx爲你的時間我在我的程序中添加了我的SetAddress和SetCity – 2010-09-01 20:07:33

1

你的代碼看起來不錯 - 它可能值得看到SetAddress的內容。我強烈推薦使用std::string而不是char *,如果citystreet沒有硬編碼到程序中,我懷疑。你會發現std::string將爲你解決內存管理和錯誤帶來的麻煩,並且通常會使得處理字符串變得更容易。

3

正如其他人回答(詹姆斯麥克奈利斯'answer想到),你應該切換到std:string而不是char *

你的問題是重複無法避免(非默認構造函數和setAddress方法設置數據),並有一個調用另一個可能不太有效。

現在,我想,真正的問題是你的代碼做了很多事情,這意味着重複精細的代碼可能是危險的和錯誤的,因此你需要有一個函數調用另一個函數。這個需求可以通過使用std::string來消除,因爲它將完全刪除代碼中的精密代碼。

因爲它沒有顯示 讓我們重新想象你的類:

class Address 
{ 
    public : 
     Address() ; 
     Address(const std::string & p_city 
       , const std::string & p_street 
       , int p_buildingNumber 
       , int p_apartmentNumber) ; 
     // Etc. 

    private : 
     std::string m_city ; 
     std::string m_street ; 
     int   m_buildingNumber ; 
     int   m_apartmentNumber ; 
} ; 

使用std::string代替const char *會使std::string對象負責處理資源(字符串本身)。

例如,你會看到我在上面的類中沒有寫入析構函數。這不是一個錯誤,因爲沒有析構函數,編譯器會生成它自己的默認函數,它將根據需要處理每個成員變量的析構函數。您用於資源處置的remove(釋放未使用的char *)也是無用的,所以它不會被寫入。這意味着很多精密的代碼不會寫入,因此不會產生錯誤。

而且它大大簡化了構造函數的實現,甚至setAddress方法:

Address::Address() 
    // std::string are initialized by default to an empty string "" 
    // so no need to mention them in the initializer list 
    : m_buildingNumber(0) 
    , m_apartmentNumber(0) 
{ 
} 

Address::Address(const std::string & p_city 
       , const std::string & p_street 
       , int p_buildingNumber 
       , int p_apartmentNumber) 
    : m_city(p_city) 
    , m_street(p_street) 
    , m_buildingNumber(p_buildingNumber) 
    , m_apartmentNumber(p_apartmentNumber) 
{ 
} 

void Address::setAddress(const std::string & p_city 
         , const std::string & p_street 
         , int p_buildingNumber 
         , int p_apartmentNumber) 
{ 
    m_city    = p_city ; 
    m_street   = p_street ; 
    m_buildingNumber = p_buildingNumber ; 
    m_apartmentNumber = p_apartmentNumber ; 
} 

不過,有重複在此代碼,而事實上,我們將不得不等待的C++ 0x到重複性較低。但至少,重複是微不足道的,並且容易遵循:沒有危險和精密的代碼,所有內容都易於編寫和閱讀。這使得你的代碼比char *版本更強大。

+0

我必須檢查城市!= NULL,並且只有當它不等於null我使用strcpy ...可以請告訴我如何使用字符串因爲我知道字符串中沒有「==」運算符? – 2010-09-01 20:43:09

+0

你知道錯了。 'std :: string'絕對有一個'=='運算符。然而,'std :: string'對象不能是'NULL'。但是,它們可以是空字符串(即'''') – 2010-09-01 20:51:17

+0

@Nadav Stern:真正的問題是:爲什麼城市可能是NULL?城市是一個字符串。它可能是空的,但不是NULL。不要在C++程序中使用'char *',除非出現一些非常特殊的情況。 – paercebal 2010-09-01 20:53:33

0

如下我可能重寫setAddress()方法:

bool Address::setAddress(const char* city, const char* street, const int buildingNumber, const int apartmentNumber) 
{ 
    return (setCity(city) 
     && setStreet(street) 
     && setBuildingNumber(buildingNumber) 
     && setApartmentNumber(apartmentNumber)) 
} 

這將實現相同的短路並返回語義,與位更少的代碼。

0

如果您必須使用char *而不是std::string,則需要自行管理字符串的內存。這包括拷貝寫在當共享文本或完整的文本副本。

下面是一個例子:

class Address 
{ 
public: 
    Address(); // Empty constructor. 
    Address(const char * city, 
      const char * street, 
      const char * apt); // Full constructor. 
    Address(const Address& addr); // Copy constructor 
    virtual ~Address(); // Destructor 
    void set_city(const char * new_city); 
    void set_street(const char * new_street); 
    void set_apartment(const char * new_apartment); 
private: 
    const char * m_city; 
    const char * m_street; 
    const char * m_apt; 
}; 


Address::Address() 
    : m_city(0), m_street(0), m_apt(0) 
{ ; } 


Address::Address(const char * city, 
       const char * street, 
       const char * apt) 
    : m_city(0), m_street(0), m_apt(0) 
{ 
    set_city(city); 
    set_street(street); 
    set_apt(apt); 
} 


Address::Address(const Address& addr) 
    : m_city(0), m_street(0), m_apt(0) 
{ 
    set_city(addr.city); 
    set_street(addr.street); 
    set_apt(addr.apt); 
} 


Address::~Address() 
{ 
    delete [] m_city; 
    delete [] m_street; 
    delete [] m_apt; 
} 


void Address::set_city(const char * new_city) 
{ 
    delete [] m_city; 
    m_city = NULL; 
    if (new_city) 
    { 
     const size_t length = strlen(new_city); 
     m_city = new char [length + 1]; // +1 for the '\0' terminator. 
     strcpy(m_city, new_city); 
     m_city[length] = '\0'; 
    } 
    return; 
} 


void Address::set_street(const char * new_street) 
{ 
    delete [] m_street; 
    m_street = NULL; 
    if (new_street) 
    { 
     const size_t length = strlen(new_street); 
     m_street = new char [length + 1]; // +1 for the '\0' terminator. 
     strcpy(m_street, new_street); 
     m_street[length] = '\0'; 
    } 
    return; 
} 


void Address::set_apt(const char * new_apt) 
{ 
    delete [] m_apt; 
    m_apt = NULL; 
    if (new_apt) 
    { 
     const size_t length = strlen(new_apt); 
     m_apt = new char [length + 1]; // +1 for the '\0' terminator. 
     strcpy(m_apt, new_apt); 
     m_apt[length] = '\0'; 
    } 
    return; 
} 

在上面的例子中,Address實例保存拷貝給定文本的。這可以防止其他實體指向相同文本並修改文本時出現問題。另一個常見問題是何時其他實體delete是存儲區域。實例仍然保存指針,但目標區域無效。

使用std::string類可以避免這些問題。代碼更小,更易於維護。看看上面的代碼與使用std::string的其他答案。

+0

然而這裏有一些問題,主要是關於異常安全。一個類不能安全地管理多個資源(或者至少在構造函數中初始化它們),請記住,如果構造函數拋出時析構函數不會被調用,而是讓您明確地手動處理已分配的資源,並且有一些try /抓住。因此,最好將每個C字符串包裝在一個適當的類中,它的唯一作用是管理內存......這是一個String類:)(無論你從哪個庫中選擇它)。 – 2010-09-02 06:46:44