使变量不可复制的紧凑方法

时间:2018-09-06 23:18:38

标签: c++ c++11

我的代码中有几个类,我需要复制一些对象。

但是,其中一些类使用了我在复制时要跳过的数据,例如所有者指针。

到目前为止,我发现这样做的唯一方法是避免完全复制并在每次需要复制一个对象时手动手工构造一个全新的对象,或者将所有者指针包装在私有的,不可复制的结构中,就像这样:

class MyClass {
    // non-copyable owner
    struct Owner {
        Owner() = default;
        ~Owner() = default;

        Owner(const Owner& o) = delete;
        Owner& operator=(const Owner& o) = delete;

        SomeOtherClass* pointer = nullptr;
    };

    Owner owner = Owner();
}

但是,这样做似乎有点冗长。

(请注意,我不希望使用std::unique_ptr,因为当对象被解构时,我不希望解构所有者)

是否有更紧凑/更有效/更易读的方法?

编辑:标记为default的构造函数等无效,是由于我没有使用默认值初始化所有者。愚蠢的。

编辑2:也许我应该澄清一些事情:所有者指针应指向拥有MyClass的对象,以作为拥有对象引用其所有者的一种方式。并非相反。这就是为什么我不希望复制指针的原因,因为具有应被复制此对象的不同所有者的对象不应更改其拥有的对象。

被接受的答案使我能够做到这一点。我希望这可以消除一些困惑。

4 个答案:

答案 0 :(得分:3)

您可以将std::unique_ptr与不执行任何操作的删除器一起使用。

template<typename T>
struct nop_deleter<T> {void operator()(T*){};};

class MyClass {
  std::unique_ptr<SomeType, nop_deleter<SomeType>> owner;
};

然后将不解构“拥有”对象。 (在这一点上,它不再是真正的“拥有”了,但这只是我的书呆子而已。)

如果您希望它更具可读性,则可以始终为类型加上别名:

template<typename T>
struct nop_deleter<T> {void operator()(T*){};};

template<typename T>
using Owner = std::unique_ptr<T, nop_deleter<T>>;

class MyClass {
  Owner<SomeType> owner;
};

答案 1 :(得分:2)

由于省略的工作方式以及较小程度的容器的工作方式,复制(和移动)具有必需的语义。

如果删除了副本或移动,那么表现良好的值类型的行为也不会出现令人惊讶的不同。当存储在std::vector中时,它们的行为也应该很好。

您的描述似乎很像auto_ptr在我们获得移动语言支持之前是如何被黑化为unique_ptr的。这是一个聪明的主意,后来变成了坏主意,auto_ptr是标准库中为数不多的不推荐使用的功能之一,因为它原来是一个坏主意。

根据其身份拆分类型的“状态”。例如,所有权是一种身份特征,而高度是一种状态特征。

将状态存储在您类型内的struct中。

身份存储在您的类型中或其他子结构中。身份子结构应具有=delete复制/移动操作。

提供一种创建具有新标识但状态相同的新对象的方法。如果对象类型为Foo,并且其状态为FooState,则您将有一个Foo(FooState)显式构造函数。

具有一种产生Foo的状态副本的方法。

不允许复制Foo。复制Foo的语义是不正常的复制语义,在复制后Foo身份会更改(清除)。 if (owner) owner->child_moving( &old, this ); for( auto* child:children ) child->parent_moving( &old, this ); 应该是不可复制的。

可以移动语义;在这种情况下,您需要向所有者报告您的位置(通过某些界面)正在发生变化-例如:

Spring-Boot

允许父母/孩子更新其所有者/孩子指针。

除非您做这种事情,否则您不想假装具有价值语义;删除您的复制/移动操作,而不是执行疯狂的操作。

答案 2 :(得分:1)

也许像工厂方法之类的东西至少在编译时避开了复制部分?

// a method of MyClass
Owner * MyOwner()
{
     // check if created
     if(uidMap.find(parentObjectUid )==uidMap.end())
     {
         // create if needed
         uidMap.insert(std::pair<size_t,Owner *>(parentObjectUid ,new Owner()));
         // you can even use shared/unique ptr instead of new Owner()
         //    if you want automatic garbage
         return uidMap[parentObjectUid ];

     }
     else
     {
          return uidMap[parentObjectUid];
     }
}

然后创建uid可能需要不需要的单调或类似的全局同步生产。因此,只要使用相同的uid复制MyClass,两个副本将具有Owner相同的单个对象。

MyClass a;
MyClass b;
Owner * o = a.MyOwner(); // => creates
Owner * p = a.MyOwner(); // => uses
Owner * q = b.MyOwner(); // => creates another
a = b; // doesn't copy anything Owner

唯一ID可以是递增的64位整数。

答案 3 :(得分:0)

不拥有资源的所有者?听起来好像您在代码中,如果我在代码中看到此错误,我认为您有错误。

也就是说,unique_ptr实际上是处理它的最佳方法。您可以为它提供一个自定义析构函数,以关闭资源(例如关闭文件句柄)。从可读的角度来看,这比拥有自定义类更容易理解。

我不知道您如何定义效率,但是,假设运行时效率高,它们没有任何作用。

最紧凑的编写方式是从NonCopyable类继承,boost有一种,但是如果您不希望依赖,编写起来就很容易。

从可读性的角度来看,我实际上会采用原始​​实现的变体,即:遵循5的规则:在您的类中指定Ctor,复制Ctor,移动ctor,复制分配和移动赋值。