[英]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)
{}
[ buildingNumber
和apartmentNumber
上的頂級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
的內容。 如果city
和street
沒有硬編碼到程序中,我強烈建議在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.