04 November 2022

C++ 核心指南目录

C.67: A polymorphic class should suppress public copy/move

理由

多态类定义或继承了至少一个虚函数。多态类很可能被当成基类用。这样的基类就可以代表多个多态子类,就能实现多态行为。如果多态类对象如果不小心以值传参,编译器会隐式地调用拷贝构造函数和赋值操作。我们可能会遇到对象切片( slicing )的风险:继承类对象的部分数据被当作基类对象的数据使用。于是,多态行为就失效了。

如果类中没有数据,请将拷贝和移动函数设置为= delete。如果有数据,则将他们设置为 protected

例子

class B { // BAD: polymorphic base class doesn't suppress copying
  public:
    virtual char m() { return 'B'; }
    // ... nothing about copy operations, so uses default ...
};

class D : public B {
  public:
    char m() override { return 'D'; }
    // ...
};

void f(B& b)
{
    auto b2 = b; // oops, slices the object; b2.m() will return 'B'
    cout << "b2.m() = " << b2.m() << "\n";
}

int main()
{
    D d;
    cout << "d.m() = " << d.m() << "\n";
    f(d);
}
d.m() = D
b2.m() = B

f()中,~D~ 类型的对象 d 被当成了 B 类型的对象使用了!

例子

class B { // GOOD: polymorphic class suppresses copying
public:
    B() = default;
    B(const B&) = delete;
    B& operator=(const B&) = delete;
    virtual char m() { return 'B'; }
    // ...
};

class D : public B {
public:
    char m() override { return 'D'; }
    // ...
};

void f(B& b)
{
    auto b2 = b; // ok, compiler will detect inadvertent copying, and protest
    cout << "b2.m() = " << b2.m() << "\n";
}

int main()
{
    D d;
    cout << "d.m() = " << d.m() << "\n";
    f(d);
}
C-src-s0pxn3.cpp: In function 'void f(B&)':
C-src-s0pxn3.cpp:27:15: error: use of deleted function 'B::B(const B&)'
   27 |     auto b2 = b; // ok, compiler will detect inadvertent copying, and protest
      |               ^
C-src-s0pxn3.cpp:13:5: note: declared here
   13 |     B(const B&) = delete;
      |     ^

这时候,编译器给出警告,基类 B 的拷贝构造函数没有定义!

注意

如果你需要多态对象的深度副本,请使用clone()函数。

例外

表示异常的对象的类既要多态,又要能够赋值拷贝构造。

强化

  • 标记有公开拷贝构造函数的多态类
  • 标记有赋值构造函数的多态类