2010-09-01 75 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