在 C++ 中通过引用传递类成员函数返回值

Posted

技术标签:

【中文标题】在 C++ 中通过引用传递类成员函数返回值【英文标题】:Passing class member function return value by reference in C++ 【发布时间】:2011-05-20 12:29:02 【问题描述】:

我一直在寻找解决此问题的方法,但似乎找不到。我确信这个一般性问题之前已经在某个地方被问过,但希望你能帮助我解决我的具体情况......

我有一个类模板someClass,其中包含以下(私有)成员:

int     size_x;
int     size_y;
int     size_total;
T *     grid;

someClass 包含一个如下所示的构造函数:

someClass (const int x, const int y)
: size_x (x), size_y (y), size_total (x*y)

    grid = new T [size_total];

如下所示的复制构造函数:

someClass (const someClass & rhs)

    size_x = rhs.size_x;
    size_y = rhs.size_y;
    size_total = rhs.size_total;
    grid = new T [size_total];
    memcpy(grid, rhs.grid, size_total*sizeof(T));

一个看起来像这样的成员函数:

T * retGrid (void) const

    return grid;

还有一个如下所示的赋值运算符:

someClass & operator= (const someClass & rhs)

    if (this != &rhs)
    
        size_x = rhs.size_x;
        size_y = rhs.size_y;
        size_total = rhs.size_total;
        grid = new T [size_total];
        memcpy(grid, rhs.grid, size_total*sizeof(T));
    

    return *this;

我正在尝试传递以下两个 someClass 对象

someClass<double> *I1 = new someClass<double>(10,10);

someClass<double> I2 = *I1;

到具有以下原型的someClass 类之外的函数:

int someFunction(double *arr);

这个调用工作正常:

int status;
status = someFunction(I1->retGrid()); // Properly working function call

但这不是

status = someFunction(&I2.retGrid()); // Compiler gives error that says "error: invalid lvalue in unary &"

如果我像这样打电话给someFunction

status = someFunction(I2.retGrid()); // Compiler gives no error but function returns error value in status

代码编译,但我得到一个运行时错误(来自someFunction 中另一个函数调用的错误状态值)。

如何正确地将I2 传递给someFunction

非常感谢...

【问题讨论】:

你需要定义一个拷贝构造函数。 谢谢 Neil,我的代码中确实有一个复制构造函数,但我没有在这篇文章中使用它。我现在编辑了帖子以包含它。 你能检查 I2 的内容吗(例如,将它们转储出来或在调试器中检查)?是否从 I1 正确复制? (我猜你的“坏状态值”可能以某种方式反映了传递的双精度数组的内容。) 复制构造函数对模板的所有实例化表现出未定义的行为,其中T 是非 POD。您应该将 memcpy 替换为对 std::copy(rhs.grid, rhs.grid+rhs.size_total, grid) 的调用。 【参考方案1】:

您正试图获取retGrid 返回的临时对象的地址(在本例中是一个指针,但这并不重要)。因此,您不能使用&amp; 方法。

如果没有与号,您可以将内部数组从I2 传递到someFunction。如果这不适合您(即,因为您遇到某种运行时错误),请考虑制作此数组的副本并将其传递给 someFunction

【讨论】:

谢谢 Xion...对于将副本传递给someFunction 的最佳方式,您有什么建议? @Evan:您不能像这样传递原始数组,并且期望在没有某种中间步骤的情况下自动获取副本。你可以直接传递你的类,也可以手动复制你的数组并传递副本。【参考方案2】:

当您的类中有指针并且必须为每个对象分配内存时,您需要定义一个复制构造函数和一个赋值运算符。您只添加了后者。这个初始化

someClass<double> I2 = *I1;

实际上是使用复制构造函数执行的,而不是使用赋值运算符。它等同于

someClass<double> I2(*I1);

但是,这是错误的,因为您只为网格分配内存。但是如果网格已经被分配(从以前的分配中),你会泄漏内存。所以它应该是这样的:

someClass & operator= (const someClass & rhs)

    if (this != &rhs)
    
        size_x = rhs.size_x;
        size_y = rhs.size_y;
        size_total = rhs.size_total;
        delete [] grid;
        grid = new T [size_total];
        memcpy(grid, rhs.grid, size_total*sizeof(T));
    

    return *this;

【讨论】:

谢谢马吕斯。我在帖子中包含了复制构造函数。我最初排除了它,但它在我的代码中。 这个赋值运算符的实现是错误的,如果表达式new T[...]中的任何东西抛出异常(你可以'不排除bad_alloc,不管T)。 是的,当然,但是对于任何类型的内存分配都是一样的 我认为 James 的(有效)观点是,在答案中显示损坏的代码并没有它应有的帮助。 @ildjam 没错。实际上,我对否决投票犹豫不决,因为提议的解决方案被破坏了,即使提出的关于初始实施的观点是有效的。【参考方案3】:

第一个问题:为什么不使用std::vector,而不是 试图自己管理内存。你不显示 析构函数;我想你释放那里的记忆。但这有 仍然存在问题:

在复制构造函数中,您使用memcpy。这不是问题 当您在double 上进行实例化时,这可能是个问题 对于其他类型。你应该使用std::copy

如果您使用的是std::vector,而retGrid 仍然需要 返回一个T*,它将是return &amp;grid[0];

赋值运算符损坏。它泄漏任何以前的 内存,如果new 失败,它会将对象留在 不一致的状态。 (必须检查自我分配是 通常暗示有问题。)一个正确的分配 操作员将在更改之前执行所有可能失败的操作 对象中的任何东西。您可以搜索有关信息 交换成语,但类似以下的内容也会 工作:

.

SomeClass&
SomeClass<T>::operator=( SomeClass const& other )

    T* newGrid = new T[other.size_total];
    std::copy( other.grid, other.grid + other.size_total, newGrid );
    delete [] grid;
    size_x = other.size_x;
    size_y = other.size_y;
    size_total = other.size_total;
    grid = newGrid;
    return *this;

如果size_total 相等,您可能需要对此进行优化 (或size_total &lt;= other.size_total)。

当然,如果你使用std::vector,编译器会生成 赋值运算符和复制构造函数就足够了;你 不用写任何东西。

您有什么理由使用I1 的指针吗? (或者是 这只是一个更大的上下文的产物,你从中 提取代码?)

关于someFunction( &amp;I2.retGrid() );someClass::retGrid() 返回一个指针。不管是否 您通过指针或对象调用该函数。服用 地址结果为T**

关于最后一次通话,没有什么会导致 您向我们展示的代码中存在问题。只要I2 是 在范围内并且没有删除它的对象,应该没有 问题。这是调用函数的正确方法 目的。您的代码中的问题在其他地方。

【讨论】:

【参考方案4】:

我不确定您遇到的运行时错误,但 status = someFunction(&amp;I2.retGrid()); 正在将指针传递给临时对象。

运行时错误可能是因为缺少在someClass&lt;double&gt; I2 = *I1; 中调用的复制构造函数

【讨论】:

【参考方案5】:

感谢大家提供的非常有用的 cmets,尤其是 AAT、David Rodriguez - dribeas、James Kanze 和 Marius Bancila 的作品。

原来问题是与someFunction 内的第三方函数的接口不正确。经过一段时间的睡眠后,我发现并修复了错误,现在调用:

status = someFunction(I2.retGrid()); // Compiler gives no error but function returns error value in status

工作正常。

但是这个讨论带来了其他非常重要的相关问题,即与我的复制构造函数和赋值运算符相关的内存管理问题以及向量的建议使用。我相信这个线程对这些优点有很大的价值。

【讨论】:

以上是关于在 C++ 中通过引用传递类成员函数返回值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中通过引用返回类对象?

在 C++ 中通过引用/值传递

如何在 C++ 中通过引用返回向量

在 C++ 中通过引用传递时参数的默认值

为啥我们需要一个默认构造函数来在 C++ 中通过引用传递一个对象?

在c ++中通过引用返回类的向量[重复]