繁体   English   中英

成员访问权限的C ++复制

[英]C++ copy on member access

我正在做一些实验,以查看何时执行复制(复制省略,RVO和NRVO情况除外)。

所以我写了一些这样的代码:

class X {
 public:
  X() { std::cout << "Default constructor" << std::endl; }

  X(const X&) { std::cout << "Copy constructor" << std::endl; }

  X(X&&) { std::cout << "Move constructor" << std::endl; }

  X& operator=(const X) {
    std::cout << "Assignment operator" << std::endl;
    return *this;
  }

  X& operator=(X&&) {
    std::cout << "Move assignment operator" << std::endl;
    return *this;
  }

  ~X() { std::cout << "Destructor" << std::endl; }
};

class Y {
 private:
  X x;

 public:
  const X& getX() const {
    std::cout << "getX" << std::endl;
    return x;
  }
};

int main() {
  Y y;
  std::cout << "assign to ref" << std::endl;
  const X& x1 = y.getX();
  (void)x1;
  std::cout << "assign to const" << std::endl;
  const X x2 = y.getX();
  return 0;
}

并且我收到以下输出:

Default constructor
assign to ref
getX
assign to const
getX
Copy constructor
Destructor
Destructor

使用gcc编译或使用-O3进行clang编译并尝试使用-std = c ++ {11,14,17}两者都产生相同的输出。

令我惊讶的是,我不希望使用y.getX()时能执行任何复制操作。 到const变量。 我经常在以下代码中使用它来简化对该变量及其成员的访问,但是我并没有在const引用上进行此操作,而是使用const希望编译器将其视为重命名。

有谁知道为什么要精确执行该副本? 我想到的唯一原因是使代码成为线程安全的。 如果有多个线程与对象y一起工作,那么我对const的分配毕竟不会是该const。 因为它将仅引用对象y中的成员x。 其他线程可能会更改。 但是我不确定这是否是真正的意图。

要查看RVO与编译器强制使用NRVO的效果,请在下面的以下修改程序上使用-fno-elide-constructors编译器开关。 使用通常的选项,您可以获得:

Default constructor 1
assign to ref
getX (with id: 1)
x1 (id:1)
assign to const
getX (with id: 1)
Copy constructor 2
x2 (id:2)
make_X copy
Default constructor 3
make_X (with id: 3)
x3 (id:3)
make_X ref
Default constructor 4
make_X (with id: 4)
x4 (id:4)
Destructor 4
Destructor 3
Destructor 2
Destructor 1

但是使用NRVO,您可以获得:

Default constructor 1
assign to ref
getX (with id: 1)
x1 (id:1)
assign to const
getX (with id: 1)
Copy constructor 2
x2 (id:2)
additional 1
Default constructor 3
make_X (with id: 3)
Move constructor 4
Destructor 3
Move constructor 5
Destructor 4
x3 (id:5)
additional 2
Default constructor 6
make_X (with id: 6)
Move constructor 7
Destructor 6
x4 (id:7)
Destructor 7
Destructor 5
Destructor 2
Destructor 1

代码示例:

#include <iostream>
int global_id;
class X {
public:
    X() : id(++global_id) {
        std::cout << "Default constructor " << id << std::endl;
    }
    X(const X&) : id(++global_id) {
        std::cout << "Copy constructor " << id << std::endl;
    }
    X(X&&) : id(++global_id) {
        std::cout << "Move constructor " << id << std::endl;
    }
    X& operator=(const X&) {
        std::cout << "Assignment operator " << id << std::endl;
        return *this;
    }
    X& operator=(X&&) {
        std::cout << "Move assignment operator " << id << std::endl;
        return *this;
    }
    ~X() {
        std::cout << "Destructor " << id << std::endl;
    }
    int id;
};

class Y {
    X x;
public:
    const X& getX() const {
        std::cout << "getX (with id: " << x.id << ')' << std::endl;
        return x;
    }
    X make_X() const {
        X extra;
        std::cout << "make_X (with id: " << extra.id << ')' << std::endl;
        return extra;
    }
};

int main()
{
    Y y;
    std::cout << "assign to ref" << std::endl;
    const X& x1 = y.getX();
    std::cout << "x1 (id:" << x1.id << ")\n";
    (void) x1;
    std::cout << "assign to const" << std::endl;
    const X x2 = y.getX();
    std::cout << "x2 (id:" << x2.id << ")\n";
    std::cout << "make_X copy" << std::endl;
    const X x3 = y.make_X();
    std::cout << "x3 (id:" << x3.id << ")\n";
    std::cout << "make_X ref" << std::endl;
    const X& x4 = y.make_X();
    std::cout << "x4 (id:" << x4.id << ")\n";
    return 0;
}

如您所见,RVO实际上只能与局部变量一起使用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM