避免对临时对象、右值析构函数进行不必要的分配
avoiding unnecessary allocations for temporaries, r-value destructor
我有很多很多字符串,它们在创建后几乎从未修改过,对它们的访问时间并不是真正的问题。我必须尽量减少内存使用,所以我选择写一个 class 而不是使用 std::string
,它必须保存额外的数据,如容量和长度以符合标准。此更改导致内存使用量减少 ~%30。
为了实现这个,我写了一个 class 只是在内部包装 C 风格的空终止字符串并管理缓冲区所以 class 只包含一个指针数据成员,它提供了足够的很少访问和使用它的界面,例如 std::string
.
我想避免样板代码并允许从 std::string
到 std::string
的透明转换。那么我可以避免 allocating/copying 来自 std::string
的数据在极端情况下,例如需要临时的时候吗?考虑一下:
class A {
public:
A(const char* p) : p_{std::strdup(p)} { }
~A() { free(p_); }
A& append(const A&);
private:
char* p_;
};
现在在下面的代码中:
A a;
a.append("test");
将创建一个临时对象,分配和复制数据。那么它将立即被销毁。问题是,有没有办法在不创建每个函数的版本的情况下避免这种情况,例如 A& append(const char*)
。我也想支持std::string
的透明转换,所以它会自动成为每个功能的3个版本。
下面的做法会有什么效果:
class A {
public:
A(const char* p) : p_{std::strdup(p)} { }
A(const char* p) &&
: p_{p} {
}
~A() { free(p_); }
~A() && { /* nothing */ }
A& append(const A&);
private:
char* p_;
};
如果这不起作用,我可以应用什么策略来解决这个问题?
What effects will the following approach have:
A(const char* p) &&
~A() &&
最有可能的影响是编译错误,因为标准不允许在 c/d-tor 上使用 ref-qualifiers。
我建议像字符串一样实现 A::c_str
,并使用模板中的以下助手:
template<class Str>
const char* c_str_help(const Str& str) {
return str.c_str();
}
const char* c_str_help(const char* str) {
return str;
}
class A {
// ...
const char* c_str() const { return p_; }
template<class Str>
A& append(const Str& str) {
const char* s = c_str_help(str);
// implement using const char* s
}
};
PS:我建议使用 unique_ptr<char, void(*)(void*)>
而不是裸指针,这样更容易实现。
有点类似于 的方法,使用 SFINAE,因此我们不会阻碍重载解析:
namespace detail_A {
char const *getString(std::string const &str) {
return str.c_str();
}
char const *getString(char const *str) {
return str;
}
// More `getString`s as needed...
template <class T>
using enableString = decltype(getString(std::declval<T>()));
}
class A {
// ...
template <class T, class = detail_A::enableString<T>>
A &append(T const &str) {
char const *p = detail_A::getString(str);
std::cout << "Appending \"" << p << "\"\n";
return *this;
}
};
我有很多很多字符串,它们在创建后几乎从未修改过,对它们的访问时间并不是真正的问题。我必须尽量减少内存使用,所以我选择写一个 class 而不是使用 std::string
,它必须保存额外的数据,如容量和长度以符合标准。此更改导致内存使用量减少 ~%30。
为了实现这个,我写了一个 class 只是在内部包装 C 风格的空终止字符串并管理缓冲区所以 class 只包含一个指针数据成员,它提供了足够的很少访问和使用它的界面,例如 std::string
.
我想避免样板代码并允许从 std::string
到 std::string
的透明转换。那么我可以避免 allocating/copying 来自 std::string
的数据在极端情况下,例如需要临时的时候吗?考虑一下:
class A {
public:
A(const char* p) : p_{std::strdup(p)} { }
~A() { free(p_); }
A& append(const A&);
private:
char* p_;
};
现在在下面的代码中:
A a;
a.append("test");
将创建一个临时对象,分配和复制数据。那么它将立即被销毁。问题是,有没有办法在不创建每个函数的版本的情况下避免这种情况,例如 A& append(const char*)
。我也想支持std::string
的透明转换,所以它会自动成为每个功能的3个版本。
下面的做法会有什么效果:
class A {
public:
A(const char* p) : p_{std::strdup(p)} { }
A(const char* p) &&
: p_{p} {
}
~A() { free(p_); }
~A() && { /* nothing */ }
A& append(const A&);
private:
char* p_;
};
如果这不起作用,我可以应用什么策略来解决这个问题?
What effects will the following approach have:
A(const char* p) && ~A() &&
最有可能的影响是编译错误,因为标准不允许在 c/d-tor 上使用 ref-qualifiers。
我建议像字符串一样实现 A::c_str
,并使用模板中的以下助手:
template<class Str>
const char* c_str_help(const Str& str) {
return str.c_str();
}
const char* c_str_help(const char* str) {
return str;
}
class A {
// ...
const char* c_str() const { return p_; }
template<class Str>
A& append(const Str& str) {
const char* s = c_str_help(str);
// implement using const char* s
}
};
PS:我建议使用 unique_ptr<char, void(*)(void*)>
而不是裸指针,这样更容易实现。
有点类似于
namespace detail_A {
char const *getString(std::string const &str) {
return str.c_str();
}
char const *getString(char const *str) {
return str;
}
// More `getString`s as needed...
template <class T>
using enableString = decltype(getString(std::declval<T>()));
}
class A {
// ...
template <class T, class = detail_A::enableString<T>>
A &append(T const &str) {
char const *p = detail_A::getString(str);
std::cout << "Appending \"" << p << "\"\n";
return *this;
}
};