关于编写代码的C ++建议

时间:2010-09-01 19:40:04

标签: 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;
}

如果我确实将char *更改为字符串还有1个问题我如何检查字符串city是否等于NULL,因为我知道字符串没有“==”运算符而字符串是一个对象且不能等于null, 我如何检查我得到的字符串是否确实是legeal。

7 个答案:

答案 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)
 {}

[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的指针的建议也很好,但这或多或少是一个单独的主题。

另一个小注:这与原始代码的基本方式不同。您的代码需要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::stringchar *没有硬编码到程序中,我强烈建议使用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查看上面的代码与其他一些答案。