我难以按照应该编写的方式编写代码。这是我的默认构造函数:
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;
}
如果我确实将char *更改为字符串还有1个问题我如何检查字符串city是否等于NULL,因为我知道字符串没有“==”运算符而字符串是一个对象且不能等于null, 我如何检查我得到的字符串是否确实是legeal。
答案 0 :(得分:15)
您应该使用std::string
而不是C字符串(const char*
)。然后您不必担心具有“删除”功能,因为std::string
将为您管理内存。
答案 1 :(得分:4)
我看到的唯一重复代码是初始化器。由于您应该都使用初始化程序而不能共享初始化程序,因此这里需要一些代码冗余。我不担心。
当新的C ++出现时,您将能够在稍后的初始化期间调用前一个构造函数。在那之前,你只需忍受这种轻微的气味。
答案 2 :(得分:3)
你可以把两个ctors结合起来:
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的指针的建议也很好,但这或多或少是一个单独的主题。
另一个小注:这与原始代码的基本方式不同。您的代码需要0或4个ctor参数。这将接受0到4之间的任何参数,因此一个人可以指定(例如)城市和街道,但不能指定建筑物编号或公寓编号。如果尝试使用1,2或3个参数对您来说非常重要,那么这种方法对您没有用。在这种情况下,额外的灵活性看起来像是对我的改进 - 例如,如果有人住在单户住宅中,省略公寓号码是合理的。
答案 3 :(得分:3)
正如其他人所回答的那样(James McNellis'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 *
版本更强大。
答案 4 :(得分:1)
您的代码看起来不错 - 可能值得查看SetAddress
的内容。如果std::string
和char *
没有硬编码到程序中,我强烈建议使用city
而不是street
s,我对此表示怀疑。你会发现std::string
会让你在内存管理和错误方面遇到麻烦,并且通常会更容易处理字符串。
答案 5 :(得分: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))
}
将实现相同的短路和返回语义,代码更少。
答案 6 :(得分: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
查看上面的代码与其他一些答案。