字符串串联和内存管理
String concatenation and memory managment
我已经使用C++一段时间了,至少有一件事我无法理解,在网上冲浪也找不到很好的解释。它与内存管理有关,可以用这个例子来说明:
考虑 std::string 的字符串连接运算符,如下所示
std::string operator+(const string& s1, string& s2(
众所周知,它返回一个新创建的对象(另一个 std::string(,其中包含连接的两个原始字符串。我的问题是:这怎么可能?这个对象在内存中的什么位置?
我的意思是,如果我必须编写函数实现,我会做这样的事情
std::string std::string::operator+(const string& s1, string& s2)
{
std::string *res=new std::string;
// fill res with the content of s1 and s2
return *res;
}
但通过这种方式,我知道我会导致内存泄漏,因为如果我调用该函数一百万次,我将生成一百万个字符串,这些字符串直到程序结束才会被释放。另一方面,我可以这样做:
std::string& std::string::operator+(const string& s1, string& s2)
{
std::string res;
// fill res with the content of s1 and s2
return res;
}
但是通过这种方式,我将返回对局部变量的引用,该变量在函数返回后立即成为垃圾。最后我可以简单地写
std::string std::string::operator+(const string& s1, string& s2)
{
std::string res;
// fill res with the content of s1 and s2
return res;
}
并按值传递结果,这应该完成任务,但在我看来效率很低,因为我必须将整个 res(理论上可能非常大(对象复制到调用函数。我这样说是因为我实际上正在研究一个线性代数库,并且执行例如矩阵加法会非常好
m3=m1+m2;
就像字符串连接一样,但如果唯一的解决方案是复制回结果对象,那么使用双矩阵(例如 100MB(是不切实际的。目前我使用的功能是
matrix& matrix::sum(matrix& m1, matrix& m2)
以这种方式使用
m3.sum(m2,m1);
这看起来很丑,也阻止我在一行中对多个矩阵求和,我必须写
m4.sum(m1,m2)
m4.sum(m4,m3)
或
(m4.sum(m1,m2)).(m4,m3)
如果我真的想在一行中制作它,但它绝对不可读。
有没有更好的方法来做所有这些事情?
提前致谢
这个版本是正确的
std::string std::string::operator+(const string& s1, string& s2)
{
std::string res;
// fill res with the content of s1 and s2
return res;
}
大多数编译器采用一种称为"返回值优化"的优化技术来处理复制值的低效率问题。这是标准明确允许的,称为复制省略。
在 C++11 中还有另一种方法:当你返回字符串res
时,它变成了一个 r 值,并且将使用 move 构造函数代替复制构造函数,而且也很便宜。但同样,大多数编译器都会优化复制和移动。
最后,我不知道为什么你必须自己实现矩阵库。如果不是家庭作业,请改用本征。优化矩阵代数是一项非常艰苦的工作,需要大量的低级理解。
正如已经指出的那样,在像重载这样的情况下 operator+
,您必须返回一个完整的对象(按值(。一般来说,这比人们想象的要小;事情像 RVO 一样,它就不再是问题了。 在(大(矩阵,另一方面,它可能成为一个严重的问题,而不是只是因为运行时,而是因为内存考虑;如果你有一个这样的表达式:
m = m1 + m2 + m3 + m4 + m5;
将有四个临时,它们都将持续到完整表达式的结尾。 如果矩阵很大,则可以施加很大的内存压力。 在这种情况下,通常技术是返回一些特殊类型,它只是保持指向左右手参数的指针; operator=
(和构造函数(然后重载以采用此类型,并构建动态的最终矩阵。 像这样:
class MatrixProxy
{
void* operator new( size_t ); // Prevent dynamic allocation
public:
virtual int rows() const = 0;
virtual int columns() const = 0;
virtual double get( int row, int column ) const = 0;
};
class MatrixOpAddResults : public MatrixProxy
{
MatrixProxy const* lhs;
MatrixProxy const* rhs;
public:
MatrixOpAddResults( Matrix const& lhs, Matrix const& rhs )
: lhs( &lhs )
, rhs( &rhs )
{
assert( lhs->rows() == rhs->rows() && lhs->columns() == rhs->columns() );
}
int rows() const override
{
return lhs->rows();
}
int columns() const override
{
return lhs->columns();
}
double get( int row, int column ) const override
{
return lhs->get( row, column ) + rhs->get( row, column );
}
};
MatrixProxy operator+( MatrixProxy const& lhs, MatrixProxy const& rhs )
{
return MatrixProxy( lhs, rhs );
}
然后,例如...
Matrix::Matrix( MatrixProxy const& other )
: m_rows( other.rows() )
, m_columns( other.columns() )
, m_data( other.rows() & other.columns() )
{
std::vector<double>::const_iterator dest = m_data.begin();
for ( int i = 0; i != m_rows; ++ i ) {
for ( int j = 0; j != m_columns; ++ j ) {
*dest = other.get( i, j );
++ dest;
}
}
}
当然,Matrix 本身应该派生自 MatrixProxy,如井。 每个运算符都需要一个 Result 类。
现代趋势是使用模板,而不是继承,在这里。 我找到了基于继承的解决方案然而,更清晰、更易于理解(因为它更明确(,至少用于解释技术,以及两者兼而有之最终应该生成完全相同的代码(前提是所有结果类中的函数是内联的(。
最后:除非这是出于个人理解,否则有免费提供几个实现矩阵的好库使用上述技术。 (闪电战++浮现在脑海中,尽管我不知道它的当前状态是什么。
现代编译器将执行"copy elision",这几乎意味着最后一个字符串示例实际上并没有复制结果字符串,它只是将结果存储在调用代码提供的位置。这显然也适用于您自己设计的vector
或matrix
。
- 当vector是tje全局变量时,c++中vector的内存管理
- 我有一个线程 1:EXC_BAD_ACCESS(代码 = 1,地址 = 0x8)错误.我认为这是由于内存管理不好.我可以
- C++将字符串传递给 C 库以进行内存管理
- 从函数返回时C++内存管理
- 函数指针和 lambda 的内存管理
- 自定义内存管理器在发布模式下工作正常,但在调试模式下则不然
- C++中的内存管理
- C和C++中的内存管理有什么区别
- 字符 * 未从重载运算符或内存管理问题正确返回
- 如何在源代码中使用执行策略检测 C++17 的扩展内存管理算法的可用性?
- 底层指针和内存管理
- 智能指针,避免使用QNetworkAccessManager时进行手动内存管理
- c++中的内存管理问题
- 使用矢量时的内存管理
- 循环和内存管理中的指针算术C++?
- C++堆栈内存管理问题
- C 内存管理中的课程如何管理 - 研究
- 不可变数据模型的内存管理
- C++ 使用数组初始化时的 STL 向量内存管理
- SFML 纹理内存管理