【问题标题】:C++,class, a function that returns an objectC++,class,一个返回对象的函数
【发布时间】:2020-05-19 14:55:25
【问题描述】:

我编写了一个名为 Point 的类,并想编写一个函数,它接受 2 个 Points 并将它们相加。

为什么我应该这样写:

Point SumPoints(Point& p1, Point& p2)
{
    Point result(p1->x + p2->x, p1->y + p2->y);
    return result;
}

在结果中定义一个指针并返回它不是更有效吗? (而不是定义一个新的 Point 'result' 然后在 main 中再次复制它)

另外,我对自动调用复制功能的时间有点困惑?是不是每次我输入=都会被调用?

【问题讨论】:

  • 是否要返回指向局部变量的指针?
  • 为什么= 会导致添加?如果有任何+ 应该导致添加,但为此,您需要重载operator+(const Point&, const Point&)

标签: c++ function class


【解决方案1】:

在结果中定义一个指针并返回它不是更有效吗?

如果你不创建一个对象,那么指针就不会指向任何东西。

从加法运算符返回一个指针也是非常混乱和非常规的。

然后在 main 中再次复制它

如果优化器设法删除副本,则根本不一定会被复制。

如果您使用结果初始化对象而不是分配,则可以通过返回纯右值来确保不会有副本:

return Point(p1->x + p2->x, p1->y + p2->y);

【讨论】:

  • 为什么“返回点(p1->x + p2->x, p1->y + p2->y);”比我帖子中的第一种方法好,我应该一直使用它吗?怎么可能没有副本?
  • @smith how it is ever possible that there won't be a copy? 因为这是指定语言的方式。该对象仅使用在 return 语句中使用的纯右值表达式进行初始化,而不是从临时对象中初始化。 should I always use it 当您可以使用prvalues并且避免复制很重要时,您应该更喜欢prvalues。
【解决方案2】:

在这里,按值返回result 是完全有效的。现代编译器将应用 NRVO named return value optimization,这样就不需要复制了。如果一个指针是通过求和操作返回的,那就更混乱了。

关于您的其他问题(如果我理解正确的话),使用= 并不总是涉及复制。例如,还有一个移动赋值运算符,它只是将rvalue 引用移动到对象中而不是复制它。也有应用复制省略的情况(与上面相同的链接)并允许完全避免复制和移动语义。

在以下示例中,您可以看到编译器几乎忽略了复制或移动 (live demo)

#include <string>
#include <iostream>
#include <iomanip>

struct Point{ 
    Point() = default;
    Point(double x, double y):x(x),y(y){}

    Point(const Point &p):x(p.x),y(p.y){
        std::cout << "copy construct" << std::endl;}

    Point(Point&& p):x(std::move(p.x)),y(std::move(p.y)){
        std::cout << "move construct" << std::endl;}

    Point& operator=(const Point& p){
        x = p.x; 
        y = p.y; 
        std::cout << "copy assign" << std::endl; 
        return *this;
    }
    Point& operator=(Point&& p){
        x = std::move(p.x); 
        y = std::move(p.y); 
        std::cout << "move assign" << std::endl; 
        return *this;
    }
    double x,y;
};

std::ostream& operator<<(std::ostream& out, const Point& p)
{
    out << std::setw(5) << p.x << std::setw(5) << p.x << std::flush;
    return out;
}

Point operator+(const Point& p1, const Point &p2)
{
    return Point(p1.x+p2.x,p1.y+p2.y);
}

Point sum1(const Point& p1, const Point &p2)
{
    return Point(p1.x+p2.x,p1.y+p2.y);
}

Point sum2(const Point& p1, const Point &p2)
{
    Point result;
    result.x = p1.x+p2.x;
    result.y = p1.y+p2.y;
    return result;
}

int main()
{
    Point p1(1,0), p2(0,1);
    auto r1 = sum2(p1,p2);
    auto r2 = sum1(p1,p2);
    auto r3 = r1+r2; 
    auto r4 = r3; // <- only this calls the copy constructor

    std::cout << "r3 = ("<< r3 << ")"<< std::endl;
}

打印出来:

copy construct
r3 = (    2    2)

作为提示,您可以重载+ 运算符,如示例所示,以更易读的方式编写求和。

【讨论】:

  • 我读了那篇文章,但仍然很困惑,编译器如何在不复制数据的情况下获取值并将其保存到另一个点...
  • @smith 没有其他值。编译器直接在需要的地方构造点。
  • @smith 如果要求编译器将副本返回到临时文件,它可能只是给您临时文件。为什么要将数据从一个地方复制到另一个地方只是为了在之后立即删除源对象。因此,省略了复制,你仍然得到了函数所承诺的返回值,每个人都很高兴。我也更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 2017-12-05
  • 1970-01-01
  • 2013-11-14
  • 2012-07-17
  • 2021-08-09
  • 2018-12-29
  • 2018-10-29
  • 1970-01-01
相关资源
最近更新 更多