C++ 有什么大惊小怪的?复制构造函数?

发布于 2024-09-12 10:39:02 字数 292 浏览 6 评论 0原文

可能的重复:
我们什么时候必须使用复制构造函数?

到底为什么C++ 复制构造函数这么重要?我刚刚了解它们,我不太明白它们有什么大惊小怪的。如果您使用指针,似乎您应该始终为您的类编写一个复制构造函数,但为什么呢?

谢谢,博达·西多。

Possible Duplicate:
When do we have to use copy constructors?

Why exactly are C++ copy constructors so important? I just learned about them and I don't quite see what is the fuss about them. It seems you should always write a copy constructor for your classes if you use pointers, but why?

Thanks, Boda Cydo.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

可可 2024-09-19 10:39:02

复制构造函数和赋值运算符在 C++ 中非常重要,因为该语言具有“复制语义”,也就是说,当您在容器中传递参数或存储值时,会传递或存储对象的副本。 C++ 如何对对象进行复制或赋值?对于本机类型,它自己知道,但对于用户定义类型,它会自动生成逐个成员的复制构造或赋值。

例如,如果您声明更明确:

class P2d
{
    public:
        double x, y;
        P2d(double x, double y) : x(x), y(y)
        { }
};

C++ 编译器自动完成您的代码,

class P2d
{
    public:
        double x, y;
        P2d(double x, double y) : x(x), y(y)
        { }

        P2d(const P2d& other) : x(other.x), y(other.y)
        { }

        P2d& operator=(const P2d& other)
        {
            x = other.x;
            y = other.y;
            return *this;
        }
};

这些自动生成的复制构造函数和赋值运算符对于您的类是否正确?在很多情况下是的......但当然也许这些实现是完全错误的。通常,例如,当您的对象中包含指针时,当您想要复制该对象时仅复制指针并不是正确的做法。

您必须了解 C++ 会进行大量对象复制,并且必须了解它将对您定义的类执行什么类型的复制。如果自动生成的副本不是您所需要的,那么您必须提供自己的实现,或者必须告诉编译器您的类应该禁止复制。

您可以通过声明私有复制构造函数和赋值运算符并且不提供实现来阻止编译器进行复制。因为这些是私有函数,并且任何要使用它们的外部代码都会收到编译器错误,并且因为您声明了它们但没有实现它们,所以如果您错误地执行了它们,您将收到链接错误最终在类实现中制作副本。

例如:

class Window
{
    public:
        WindowType t;
        Window *parent,
               *prev_in_parent, *next_in_parent,
               *first_children, *last_children;
        Window(Window *parent, WindowType t);
        ~Window();

    private:
        // TABOO! - declared but not implemented
        Window(const Window&); // = delete in C++11
        Window& operator=(const Window&); // = delete in C++11
};

如果最后一部分看起来很荒谬(你怎么能在实现中错误地进行复制),请注意,在 C++ 中,很容易错误地进行额外的复制,因为该语言是围绕复制事物的概念构建的。

一条黄金法则是,如果您的类有一个析构函数(因为它需要进行一些清理),那么很可能逐个成员复制不是正确的做法……而且如果您有特殊的逻辑来执行复制构造,那么在赋值中可能也需要类似的逻辑(反之亦然)。因此,这条被称为“三巨头”的规则规定,要么您的类没有自定义析构函数、没有复制构造函数、没有赋值运算符,要么您的类应该拥有这三个函数。

这条规则非常重要,例如,如果对于任何特殊情况,您最终得到一个只需要析构函数的类(我想不出一个合理的情况......但我们只能说您找到了一个),那么请记住添加为您考虑过的评论,您知道隐式生成的复制构造函数和赋值运算符是可以的。
如果您不添加有关其他两个的注释,那么任何阅读您的代码的人都会认为您只是忘记了它们。

UPDATE

C++ 正在不断发展,虽然这里所说的大部分内容现在仍然有效,但该语言提供了一种更好的方法来通知编译器不应允许复制和赋值。

新语法(自 C++11 起有效)是

struct Window {
    ...
    Window(const Window&) = delete;
    Window& operator=(const Window&) = delete;
};

Copy constructors and assignment operators are very important in C++ because the language has "copy semantics", that is to say when you pass a parameter or store a value in a container, a copy of the object is passed or stored. How can C++ make a copy or perform an assignment on an object? For native types it knows by itself, but for user-defined types instead it automatically generates a member-by-member copy construction or assignment.

More explicitly if you declare for example:

class P2d
{
    public:
        double x, y;
        P2d(double x, double y) : x(x), y(y)
        { }
};

the C++ compiler automatically completes your code to

class P2d
{
    public:
        double x, y;
        P2d(double x, double y) : x(x), y(y)
        { }

        P2d(const P2d& other) : x(other.x), y(other.y)
        { }

        P2d& operator=(const P2d& other)
        {
            x = other.x;
            y = other.y;
            return *this;
        }
};

Are these automatically generated copy constructor and assignment operators correct for your class? In many cases yes... but of course maybe those implementations are totally wrong. Quite often for example when you have pointers contained inside your objects then just copying the pointer when you want to make a copy of the object is not the right thing to do.

You must understand that C++ does a lot of copies of objects, and you must understand what type of copy it will do for classes you defined. If the automatically generated copy is not what you need then you must either provide your own implementation, or you must tell the compiler that copying should be forbidden for your class.

You can prevent the compiler from making copies by declaring a private copy constructor and assignment operator, and by not providing an implementation. Because those are private functions and any external code that is going to use them will get a compiler error, and because you declared them but you didn't implement them you will get a link error if by mistake you end up making copies inside the class implementation.

For example:

class Window
{
    public:
        WindowType t;
        Window *parent,
               *prev_in_parent, *next_in_parent,
               *first_children, *last_children;
        Window(Window *parent, WindowType t);
        ~Window();

    private:
        // TABOO! - declared but not implemented
        Window(const Window&); // = delete in C++11
        Window& operator=(const Window&); // = delete in C++11
};

If the last part seems absurd (how can you make copies in the implementation by mistake) please note that in C++ it is very very easy to make extra copies by mistake because the language has been built around the concept of copying things around.

A golden rule is that if your class has a destructor (because it needs to do some cleanup) then most likely a member-by-member copy is not the right thing to do... and also if you have special logic to do a copy construction then a similar logic is probably needed also in assignment (and vice versa). So the rule, known as the Big Three, states that either your class has no custom destructor, no copy constructor, no assignment operator or your class should have all three of them.

This rule is so important that for example if for any special case you end up with a class that just needs a destructor (I can't think a sensible case... but let's just say you found one) then please remember to add as a comment that you thought about it and you know that the implicitly generated copy constructor and assignment operators are ok.
If you don't add a note about the other two, whoever will read your code will think that you simply forgot about them.

UPDATE

C++ is evolving and while most of what is said here is still valid now the language provides a better method to inform the compiler that copying and assignment shouldn't be allowed.

The new syntax (valid since C++11) is

struct Window {
    ...
    Window(const Window&) = delete;
    Window& operator=(const Window&) = delete;
};
困倦 2024-09-19 10:39:02

简单:当 C++ 使用默认的复制构造函数时,它会复制指针,但复制所指向的数据。结果:两个对象指向相同的数据。如果两者都认为自己拥有该数据,并在调用其析构函数时删除指针,那么您就会遇到一大堆麻烦......

Simple: when C++ uses a default copy constructor, it copies the pointer, but not the data being pointed to. The result: two objects that point to the same data. If both think they own that data, and delete the pointer when their destructor is called, you've got a heap of trouble...

有深☉意 2024-09-19 10:39:02

为什么 C++ 是复制构造函数
这么重要?

大多数其他语言都不需要复制构造函数,因为它们要么:

  • 没有指针(例如,旧版本的 BASIC),在这种情况下复制对象总是安全的,或者
  • 除了指针什么都没有/引用(例如,Java、Python),在这种情况下复制很少见,然后可以使用 copy()clone() 方法来完成。

C++ 更喜欢值语义,但也使用大量指针,这意味着:

  • 对象被大量复制,并且
  • 出于其他人提到的原因,您必须指定是想要浅复制还是深复制。

Why exactly are C++ copy constructors
so important?

Copy constructors aren't needed in most other languages because they either:

  • Don't have pointers (e.g., old versions of BASIC), in which case copying objects is always safe, or
  • Have nothing but pointers/references (e.g., Java, Python), in which case copying is rare, and then can be done with a copy() or clone() method.

C++ prefers value semantics but also uses a lot of pointers, which means that:

  • Objects get copied a lot, and
  • You have to specify whether you want a shallow or deep copy, for reasons the others have mentioned.
佞臣 2024-09-19 10:39:02

C++ 中的每个类都有一个隐式复制构造函数,用于执行对象的浅复制。浅意味着它复制了成员的值。因此,如果存在指针,则复制指针值,以便两个对象都指向相同的对象。

大多数时候这是不需要的,因此您必须定义自己的复制构造函数。

通常,您甚至不想创建对象的副本,因此声明复制构造函数但不定义它是一种很好的风格。 (头文件中的空声明)。

然后,如果您不小心创建了一个副本(例如返回一个对象、在声明参数引用方法时忘记了 & 等),您将收到链接器错误。

如果将复制构造函数声明为私有,您还会收到编译器错误(如果在类外部使用)。

总结一下:显式声明复制构造函数总是很好的风格 - 特别是如果您根本不需要:只需

private:
  MyClass(const MyClass&);

在类定义中写入即可禁用类的复制构造函数。

Every class in C++ has an implicit copy constructor that does a shallow copy of the object. Shallow means it copies the value of the members. So if there's a pointer the pointer value is copied so both object point to the same.

Most of the time this is not wanted, so you have to define your own copy constructor.

Often you don't even want to create copies of your object, so it's good style to declare a copy constructor, but not define it. (Empty declaration in header file).

Then, if you accidentially create a copy (e.g. returning an object, forgot the & when declaring a parameter-by-reference method etc.), you'll get a linker error.

If you declare the copy constructor private, you'll also get a compiler error (if used outside the class).

To summarize it: It's always good style to declare the copy constructor explicitly - especially if you don't need on at all: Just write

private:
  MyClass(const MyClass&);

in your class definition to disable the copy constructor of your class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文