c – 字符串连接和内存管理

我已经使用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)

如果我真的想在单行中制作它,但它绝对不可读.

有没有更好的方法来做这些事情?

提前致谢

最佳答案
正如已经指出的那样,在超载的情况下
运算符,您必须返回一个完整的对象(按值).
一般来说,这不是人们想象的问题;事
像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派生而来
好.你需要为每个运算符提供一个Results类.

现代趋势是使用模板,而不是
继承,在这里.我找到了基于继承的解决方案
然而,更清晰,更容易理解(因为它更多
显式),至少用于解释技术,以及两者
应该最终产生完全相同的代码(提供全部
结果类中的函数是内联的).

最后:除非这是为了个人理解,否则有
几个可以免费获得的实现矩阵的好库
使用上述技术. (闪电战浮现在脑海中,尽管如此
我不知道它目前的状况.)

转载注明原文:c – 字符串连接和内存管理 - 代码日志