将结构的所有元素归零的最佳方法

时间:2017-07-21 12:03:15

标签: c++ c++11

我有一个经常在我的代码中使用的结构:

struct HttpResponse{

string header;
string response;
int statusCode;

HttpResponse():header(""),response(""),statusCode(0){}
HttpResponse(string header,string response,int statusCode):header(header),response(response),statusCode(statusCode){}
};

我需要确保所有字符串都被初始化为""和整数为(0)。每次我需要使用默认构造函数创建结构或创建它一次并在每次使用后将其所有成员归零是我可以看到的选项。哪种方法更有效?

5 个答案:

答案 0 :(得分:4)

正常的做法是在必要时构建默认构造函数。在您的情况下,这是必要的,否则statusCode将保持未初始化。 (将为您调用std::string构造函数)。

HttpResponse() : statusCode(0){}就足够了。

你可以自己构建某种clear方法;但这将需要您考虑所有类成员,而不仅仅是普通的旧数据类型。这比较难维护。

答案 1 :(得分:2)

这样做的最佳方式取决于一些事情。因此,您必须使用合理的基准测试输入对程序进行基准测试。

然而,有一些一般性的考虑因素:

首先,构造和删除字符串可能有些昂贵,因为它可能涉及在堆上分配和释放内存。然而,它并不是非常昂贵,取决于实施。我期待保持"响应"对象要快一点。

另一方面,保持这些缓存对象具有潜在的缺点:

  1. 您可能忘记打电话给清楚,并创建一个难以找到的错误。 这取决于代码中缓存响应的使用次数。
  2. 对缓存对象的引用可能会转义并存储的时间超出您的想象,或者您的程序变为多线程,这可能会导致意外的别名,甚至更糟糕的错误。
  3. 使用本地静态响应意味着可以在函数开头添加(可能非常有效)初始化检查。
  4. 使用由指针传入的缓存响应(通过this指针显式或隐式地作为对象的成员)可能会使编译器的别名分析更加谨慎并阻止其他优化。当内联所有代码时,也可以防止编译器在临时响应上消除未使用的计算。
  5. 在这些潜在的缺点中,我最初只担心1和2,只有在分析证明它们可能相关时才考虑其他的。

    如果代码被证明是关键的,我建议的另一个步骤是查看正在生成的代码:

    https://godbolt.org/g/SFCc23

    我在这里学到的一件事是,使用gcc,使用默认构造函数初始化字符串会更有效,这使得它变为空,而不是使用string("")

    #include <string>
    
    using std::string;
    
    struct HttpResponse{
    
    string header;
    string response;
    int statusCode;
    
    HttpResponse():header(),response(),statusCode(0){}
    HttpResponse(string header,string response,int statusCode):header(header),response(response),statusCode(statusCode){}
    void clear()
    {
        header.clear();
        response.clear();
        statusCode = 0;
    }
    };
    
    void handleResponse(HttpResponse &r);
    
    void test()
    {
        HttpResponse r;
        handleResponse(r);
    }
    
    void test2()
    {
        static HttpResponse r;
        r.clear();
    
        handleResponse(r);
    }
    
    void test3(HttpResponse *theCachedResponse)
    {
        theCachedResponse->clear();
        handleResponse(*theCachedResponse);
    }
    

    在这种情况下,为第三个变体生成的代码看起来比其他变体的代码短一些。请注意,test2只需要一次长的初始化路径,因此即使是最差的分支预测器,也可以很好地预测开始时的测试和跳转,因此在较大的CPU上几乎不需要时间。如果重复调用该函数,则初始化将不相关。

答案 2 :(得分:1)

正如Bathsheba所说,使用默认构造函数。当且仅当您遇到性能问题(并且您可以证明使用分析器)时,您可以对此进行优化。请不要过早优化。

答案 3 :(得分:1)

不幸的是,我不同意上面的同事。您不应该释放和重新分配内存只是为了重置类的状态。这非常低效。对于你所要求的,即破坏和重建或使用“清晰”方法是否更有效?我认为明确的答案是第二个

答案 4 :(得分:1)

这个问题的常见答案是使用构造函数,每次需要时都创建结构。因为另一个解决方案是&#34;过早优化&#34;。

但是,第二种变体可能更好,我不认为它是过早的优化。写起来并不难,不难理解,也不难维护。只是一个不同的解决方案(可能有它的缺点。例如,它不能在多线程环境中使用。)

为什么第二种变体会更好?因为你可以为你的字符串避免内存分配/免费(当前的实现不会为string::clear释放内存,所以如果字符串不增长,你将避免不必要的内存操作)。