簡體   English   中英

有關編寫代碼的C ++建議

[英]C++ advice on writing code

我在編寫代碼時遇到困難。 這是我的默認構造函數:

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 *而我的setter使用remove來設置一個新城市。 我非常想聽聽您對如何以正確的方式編寫而不重復代碼的意見。 這是我的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 *更改為string,我如何檢查string city不等於NULL,因為我知道string沒有“ ==”運算符,並且string是一個對象,並且不能等於null,我怎么能檢查我得到的字符串是否確實是合法的。

您應該使用std::string而不是C字符串( const char* )。 然后,您不必擔心具有“刪除”功能,因為std::string將為您管理內存。

我看到的唯一重復的代碼是初始化程序。 由於您既應該使用初始化程序,又不能共享初始化程序,所以這里需要一些代碼冗余。 我不會擔心。

當新的C ++發布時,您將能夠在后一個初始化期間調用前一個構造函數。 在此之前,您只需要忍受這種輕微的氣味即可。

您可以組合兩個ctor:

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上的頂級const沒有完成任何操作,並嘗試將實現信息移入接口,因此我將其刪除。

的,如果您真的喜歡:

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指針的建議也是一個很好的建議,但這或多或少是一個獨立的主題。

還有一點需要注意的地方:這與原始代碼在根本上有所不同。 您的代碼要求ctor的參數為0或4。 該參數可以接受0到4之間的任意值,因此人們可以指定(例如)城市和街道,但不能指定建築物號或公寓號。 如果拒絕使用1、2或3個參數對您來說確實很重要,那么這種方法對您將無用。 在這種情況下,額外的靈活性對我來說似乎是一種進步-例如,如果某人住在單戶住宅中,則省略公寓號碼是很合理的。

正如其他人回答的那樣(想到了詹姆斯·麥克尼利斯的答案 ),您應該切換到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 *版本更健壯。

您的代碼看起來不錯-值得看一下SetAddress的內容。 如果citystreet沒有硬編碼到程序中,我強烈建議在char * s上使用std::string ,我對此表示懷疑。 您會發現std::string可以省去內存管理和錯誤的麻煩,並且通常會使處理字符串變得更加容易。

我可能會如下重寫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))
}

它將以更少的代碼實現相同的短路和返回語義。

如果必須使用char *而不是std::string ,則需要自己管理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的其他一些答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM