禁止赋值和按值传递

发布于 2024-12-29 23:28:17 字数 300 浏览 2 评论 0原文

据我了解,我可以通过定义私有复制构造函数和赋值运算符来“禁用”复制和分配给我的对象:

class MyClass
{
private:
    MyClass(const MyClass& srcMyClass);
    MyClass& operator=(const MyClass& srcMyClass);
}

但这有什么用?
这被认为是一种不好的做法吗?

如果您能描述这种情况,我将不胜感激,在这种情况下,以这种方式“禁用”赋值和复制构造函数是合理/有用的。

From what I understand I'm able to "disable" copying and assigning to my objects by defining private copy constructor and assignment operator:

class MyClass
{
private:
    MyClass(const MyClass& srcMyClass);
    MyClass& operator=(const MyClass& srcMyClass);
}

But what's the usage of this?
Is it considered a bad practice?

I would appreciate if you could describe the situation, in which it would be reasonable / useful to "disable" assignment and copy constructor in this way.

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

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

发布评论

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

评论(5

三生殊途 2025-01-05 23:28:17

当复制对象没有意义时,它非常有用。这绝对不被认为是不好的做法。

例如,如果您有一个代表网络连接的类,则复制该对象是没有意义的。另一次,您可能希望某个类不可复制,如果您有一个类代表多人游戏中的一个玩家。这两个类都代表在现实世界中无法复制的事物,或者复制没有意义的事物(一个人,一个联系)。

另外,如果您尝试实现单例,则使对象不可复制是标准过程。

It's useful when it doesn't make sense for your object to be copied. It is definitely not considered bad practice.

For instance, if you have a class that represents a network connection, it's not meaningful to copy that object. Another time you may want a class to be noncopyable is if you had a class representing one player in a multiplayer game. Both these classes represent things that can't be copied in the real world, or that don't make sense to copy (a person, a connection).

Also, if you are trying to implement a Singleton, it's standard procedure to make the objects non-copyable.

遇见了你 2025-01-05 23:28:17

一般来说,管理资源的任何类都应该是不可复制的或具有专门的复制语义。反之亦然:任何不可复制或需要专门复制语义的类都在管理资源。 C++语言中的“管理资源”实际上意味着负责内存中的某些空间,或者负责与网络或数据库的连接,或者文件的句柄,或者撤消事务,等等。

资源管理有很多例子。这些职责需要执行前缀操作、后缀操作以及可能介于两者之间的某些操作。例如,内存管理涉及获取我们将管理的内存地址的句柄,也许会弄乱该内存,最后释放该句柄(因为如果你喜欢某样东西,就让它自由)。

template<typename T>
struct memory {
    memory(T const& val = T()) : p(new T(val)) { } 
    ~memory() { delete p }
    T& operator*() const { return *p; }
private:
    T* p;
};

// ...
{
    memory<int> m0;
    *m0 = 3;
    std::cout << *m0 << '\n';
}

这个内存类几乎是正确的:它自动获取底层内存空间并自动释放它,即使异常在获取资源后传播一段时间也是如此。但考虑一下这种情况:

{
    memory<double> m1(3.14);
    memory<double> m2(m1);  // m2.p == m1.p (do you hear the bomb ticking?)
}

因为我们没有为内存提供专门的复制语义,所以编译器提供了自己的复制构造函数和复制赋值。它们做了错误的事情:m2 = m1 意味着m2.p = m1.p,这样两个指针指向同一个地址。这是错误的,因为当 m2 超出范围时,它会像一个好的负责任的对象一样释放其资源,而当 m1 超出范围时,它也会释放其资源,相同的资源m2 已经释放,完成了双重删除——一个臭名昭著的未定义行为场景。此外,在 C++ 中,复制对象非常容易,甚至不会注意到:一个函数按值获取其参数,按值返回其参数,或通过引用获取其参数,然后调用另一个本身获取(或返回)其参数的函数按值传递参数...更容易假设事物尝试被复制。

所有这些都表明,当类存在的理由是管理资源时,您应该立即知道您需要处理复制。您应该决定

  • 支持复制,同时决定复制的含义:安全共享资源、执行底层资源的深层复制,这样就不会发生任何共享,或者将两种方法结合起来,如 写时复制 或惰性复制。无论您选择什么路径,都需要提供专门的复制构造函数复制赋值运算符。
  • 或者您不支持任何类型的资源复制,在这种情况下,您禁用复制构造函数和复制赋值运算符。

到目前为止,我想说资源管理是禁用复制或提供专门的复制语义的唯一情况。这只是三法则的另一个视角。

Generally speaking any class that manages a resource should be none-copyable or have specialized copy semantics. The converse is true as well: any class that is non-copyable or needs specialized copy semantics is managing a resource. "Manage a resource" in the C++ lingua in practice means responsible for some space in memory, or for a connection to a network or a database, or a handle to a file, or an undo transaction, and so on.

Resource management captures quite a lot of examples. These are responsibilities that take a prefix operation, a suffix operation and possibly some action in between. Memory management, for example, involves acquiring a handle to a memory address which we'll manage, perhaps mess around with that memory, and finally release the handle (because if you love something, let it be free).

template<typename T>
struct memory {
    memory(T const& val = T()) : p(new T(val)) { } 
    ~memory() { delete p }
    T& operator*() const { return *p; }
private:
    T* p;
};

// ...
{
    memory<int> m0;
    *m0 = 3;
    std::cout << *m0 << '\n';
}

This memory class is almost correct: it automatically acquires the underlying memory space and automatically releases it, even if an exception propagates some time after it acquired its resource. But consider this scenario:

{
    memory<double> m1(3.14);
    memory<double> m2(m1);  // m2.p == m1.p (do you hear the bomb ticking?)
}

Because we didn't provide specialized copy semantics for memory, the compiler provides its own copy constructor and copy assignment. These do the wrong thing: m2 = m1 means m2.p = m1.p, such that the two pointers point at the same address. It's wrong because when m2 goes out of scope it frees its resource like a good responsible object, and when m1 then goes out of scope it too frees its resource, that same resource m2 has already freed, completing a double-delete -- a notorious undefined behaviour scenario. Moreover, in C++ it's extremely easy to make copies of an object without even noticing: a function taking its parameter by value, returning its parameter by value, or taking its parameter by reference but then calling another function which itself takes (or returns) its parameter by value ... It's easier to just assume that things will try to get copied.

All this to say that when a class' raison d'être is managing a resource then you immediately should know that you need to handle copying. You should decide

  • you support copying, whereas you decide what copying means: safe sharing of the resource, performing a deep copy of the underlying resource so there is no sharing whatsoever, or combining the two approaches as in copy-on-write or lazy copy. Whatever path you choose you will need to provide a specialized copy constructor and a copy assignment operator.
  • or you don't support any sort of copying of the resource, in which case you disable the copy constructor and the copy assignment operator.

I'd go so far and say that resource management is the only case where you disable copying or provide specialized copy semantics. This is just another perspective on The Rule of Three.

笑叹一世浮沉 2025-01-05 23:28:17

这是一种很常见的做法。有很多例子表明复制是不合适的。

假设您的对象代表一个开放的服务器端套接字(即传入的网络连接);制作该对象的副本的语义是什么?

It's a pretty common practice. There are are lots of examples where copying isn't appropriate.

Let's say your object represents an open server-side socket (i.e. an incoming network connection); what would be the semantics of making a copy of that object?

情栀口红 2025-01-05 23:28:17

当您仅在检查后才被允许创建对象实例时,就像在单例的情况下一样,您需要私有构造函数。当调用构造函数时,将调用对象实例,然后检查是否已经存在另一个实例是没有意义的。所以我们要做的就是从 main 调用类的成员函数,并在该成员函数内部检查另一个实例是否已在内存中。如果没有调用构造函数。否则就中止了。
检查单例类或其他受保护类,其中对象的数据必须保持安全并且不允许复制。

另请检查:C++ 中的单例类

when you are allowed to create instance of object only after checking like in the case of singleton u need private constructors. when constructor is called the object instance will be called and then there is no point in checking if there is another instance already. so what we do is call a member function of class from main and inside that member function check if another instance is already in the memory. if not constructor is called. else aborted.
check singleton classes or other protected classes where data of object has to be kept secured and should not be allowed to copy.

also check this : Singleton Class in C++

撕心裂肺的伤痛 2025-01-05 23:28:17

当您尝试实现单例模式时,使用私有构造函数是完全可以接受的,因为它只能在其自身内部实例化,而不能从其他地方实例化。
一旦调用,构造函数就无法撤销。因此,只有在检查单例条件是否满足后才会调用构造函数。

When you are trying to implement a singleton pattern it's perfectly acceptable to use a private constructor as it's meant to be only instantiated inside itself and from nowhere else.
Once invoked, the constructor can't be revoked. So the constructor is invoked only after checking if the singleton condition satisfied.

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