C++ string或const char*,哪一个用作构造函数参数更有效
我发现C++ string或const char*,哪一个用作构造函数参数更有效,c++,C++,我发现sizeof(string)对于VC和GCC分别是28B和32B(不包括堆上分配的数据部分)。这使我非常怀疑使用字符串作为C++构造函数的参数的效率。所以我做了一个小实验如下: class Foo { static int inc; int val; int id; public: Foo(int value) :val(value), id(Foo::inc++) { std::cout << to_string() <&
sizeof(string)
对于VC和GCC分别是28B和32B(不包括堆上分配的数据部分)。这使我非常怀疑使用字符串作为C++构造函数的参数的效率。所以我做了一个小实验如下:
class Foo
{
static int inc;
int val;
int id;
public:
Foo(int value) :val(value), id(Foo::inc++) {
std::cout << to_string() << " being constructed\n";
}
~Foo() {
std::cout << to_string() << " being destroyed\n";
};
Foo(Foo const &other) :Foo(other.val) {
std::cout << other.to_string() << " being copied\n";
}
Foo& operator=(Foo const &other) {
val = other.val;
std::cout << other.to_string() << " being copied\n";
}
Foo(Foo &&other) :Foo(other.val) {
std::cout << other.to_string() << " being moved\n";
}
Foo& operator=(Foo &&other) {
val = other.val;
std::cout << other.to_string() << " being moved\n";
}
int value() const noexcept { return val; }
std::string to_string() const {
return std::string("Foo_") + std::to_string(id) + "_(" + std::to_string(val) + ")";
}
};
int Foo::inc = 0;
struct Bar {
Bar(Foo const &foo) :val(foo) {}
Bar(Foo &&foo) :val(std::move(foo)) {}
private:
Foo val;
};
//------- run it ------
Bar bar {42};
显然,临时的Foo实例是在ctor的参数处创建的。因此,我假设当我将一个const char*
传递给一个需要字符串的构造函数时,同样的过程也会发生,也就是说,一个temp str>=28/32字节会在移动/复制后立即被创建和删除。这样的花费让我很不舒服。别误会,我将使用字符串作为类的数据成员,我只担心ctor的形式参数类型
无论如何,如果我将string
参数替换为const char*
,考虑到我总是可以求助于string::c_str()?我想听听你的看法。如果我的分析有什么问题,请指出。如果有某种机制可以消除临时字符串的成本,请教我如何工作。如果临时字符串不可避免,我是否必须同时使用const char*
和string&
来重载ctor,一个用于避免临时字符串,另一个用于右值字符串以避免深度复制?提前谢谢 请注意,您的问题明确提到了构造函数参数,因此此答案引用了这些参数
其他推理适用于赋值运算符和设置器。(有关更多详细信息,请参阅)
如果要将字符串作为数据成员存储在对象中,则不可避免地必须创建字符串
因此,最简单的选择是创建字符串作为构造函数参数的副作用,然后将其移动到数据成员中
在以下示例中,不会创建并销毁临时字符串。所有分配的资源都存储在数据中
(通过使用move)
根据sizeof
分析,请注意此大小与动态(堆)存储无关std::string
有时会分配用于存储字符数据
这只是内部数据成员的大小,这些成员在堆栈上分配(自动存储持续时间),这非常快
堆栈分配通常不应该引起您的任何关注。请注意,您的问题明确提到了构造函数参数,因此此答案引用了这些参数
其他推理适用于赋值运算符和设置器。(有关更多详细信息,请参阅)
如果要将字符串作为数据成员存储在对象中,则不可避免地必须创建字符串
因此,最简单的选择是创建字符串作为构造函数参数的副作用,然后将其移动到数据成员中
在以下示例中,不会创建并销毁临时字符串。所有分配的资源都存储在数据中
(通过使用move)
根据sizeof
分析,请注意此大小与动态(堆)存储无关std::string
有时会分配用于存储字符数据
这只是内部数据成员的大小,这些成员在堆栈上分配(自动存储持续时间),这非常快
堆栈分配通常不应该引起您的任何关注。std::string并不是最便宜的内存类,因为它优化了小对象。这使得使用它确实有问题,尽管它也有很好的API函数和很多安全性/可用性
你应该担心吗?
如果代码不是性能关键的,不要担心!在程序的生命周期中获得几微秒是不值得的
相反,在您的代码上运行一个探查器,并修复您可以找到的瓶颈
通过值/构造引用传递std::string
假设您使用的是优化编译器并传递标志O2,O3。。。在许多情况下,编译器可以消除开销。如果需要,在标头中实现For。如果通过值传递,请不要忘记std::move
正在传递std::string\u视图
在标准库的较新标准中,string_视图可用,如果不可用,您可以轻松地从GSL复制它。。。
此类类似于原始字符指针(+size),可用于需要std::string传递字符串的位置
最后,如果您想存储字符串,您仍然需要转换为std::.stringstd::string并不是最便宜的内存类,因为小型对象优化。这使得使用它确实有问题,尽管它也有很好的API函数和很多安全性/可用性
你应该担心吗?
如果代码不是性能关键的,不要担心!在程序的生命周期中获得几微秒是不值得的
相反,在您的代码上运行一个探查器,并修复您可以找到的瓶颈
通过值/构造引用传递std::string
假设您使用的是优化编译器并传递标志O2,O3。。。在许多情况下,编译器可以消除开销。如果需要,在标头中实现For。如果通过值传递,请不要忘记std::move
正在传递std::string\u视图
在标准库的较新标准中,string_视图可用,如果不可用,您可以轻松地从GSL复制它。。。
此类类似于原始字符指针(+size),可用于需要std::string传递字符串的位置
最后,如果你
Foo_0_(42) being constructed
Foo_1_(42) being constructed
Foo_0_(42) being moved
Foo_0_(42) being destroyed
Foo_1_(42) being destroyed
class Foo
{
public:
Foo(std::string data)
: data_(std::move(data))
{}
private:
std::string data_;
};
int main()
{
const char* foo = "...";
Foo a(foo);
std::string bar = "...";
Foo b(std::move(bar));
Foo c("...");
}