C++ 中的替代复制构造函数

alternative copy constructor in c++

提问人:ugo_capeto 提问时间:3/30/2023 最后编辑:ugo_capeto 更新时间:6/6/2023 访问量:184

问:

编辑: 我将介绍一些设计糟糕的类,这是一个老问题,从那时起我就成长为一名程序员......

假设我有一个具有某种状态的类,并且使用一个仅使用该状态的一部分的方法,您将明白我的意思:

#include <thread>
#include <chrono>

class MyClass 
{
public:
    MyClass(double _1, double _2, double _3)
        : data1(_1)
        , data2(_2)    
        , data3(_3) 
    {}
    void only_uses_data1()
    {
        for (int i = 0; i < data1; ++i)
            std::this_thread::sleep_for(std::chrono::seconds(1));
        data1 = 0; // modifies state
    }
    auto time_the_method() const // this can't modify the instance
    {
        MyClass copy = *this;
        auto t1 = std::chrono::high_resolution_clock::now();
        copy.only_uses_data1();
        auto t2 = std::chrono::high_resolution_clock::now();
        return t2 - t1;
    }
private:
    double data1, data2, data3;
};

如果你能原谅我这个愚蠢的例子,我想指出也只使用,所以我当时的想法是创建一个函数来创建一个新实例,该实例只复制方法中所需的元素,我想到了这样的事情:time_the_methoddata1MyClass

// inside MyClass definition
private:
explicit MyClass(const MyClass& other, int) // takes a dummy int
    : data1(other.data1) // copies the necessary data
{}
// usage
MyClass copy(*this, 0);

我创建了当时所谓的“自定义复制构造函数”(这个名字激怒了一些人),它需要一个虚拟变量来更改构造函数的签名。 此外,虚拟变量实际上将来可能会有一些用处,也许可以切换到一个,这样我就可以告诉编译器,可能在编译时使用模板,当我想要在我的成员函数中进行部分复制时,我需要哪个“自定义复制构造函数”。enum class

我有这个想法是因为我注意到,在覆盖增量前和增量后运算符时,我们必须做类似的事情:

MyClass operator++(int); // pre increment
MyClass& operator++();   // post increment

通过更改签名,编译器能够知道要调用哪个签名。

我还意识到,这种技术更适合于操作员重载,而不是构造函数重载。 这就是为什么我问是否有人在这方面有任何改进。

我意识到,如果你必须做这样的事情,很可能类本身设计得很差,它处理了太多的状态,所以需要代码重构。但无论如何,探索次优解决方案并没有什么坏处,也许只是为了理解为什么最优解决方案真的更好......

C++ 优化 有复制构造函数

评论

0赞 Samuel Liew 3/31/2023
评论已移至聊天室;请不要在此处继续讨论。在下方发表评论之前,请查看评论的目的。不要求澄清或提出改进建议的评论通常属于 Meta Stack Overflow 或 Stack Overflow Chat 中的答案。继续讨论的评论可能会被删除。

答:

1赞 463035818_is_not_an_ai 3/31/2023 #1

我确实明白您的代码只是一个示例,尽管我不明白是什么。请注意,它不是复制构造函数。复制构造函数不是别的东西。这种区别很重要,因为大多数情况下,复制构造函数是在创建复制时隐式调用的:MyClass(const MyClass& other, int)MyClass(const MyClass&)

 void foo(Example);
 Example a;
 Example b = a; // calls the copy constructor
 foo(b);        // calls the copy constructor

MyClass(const MyClass& other, int)不是复制构造函数。如果你对此感到满意,那就好了。它只是不是一个在制作副本时会隐式调用的构造函数。

我知道传递虚拟变量与重载前缀和后缀 ++运算符的过程相同:

利用过载分辨率是个好主意。虽然你不会得到与. 需要特殊的语言支持来区分 和 。构造函数无法获得该支持。++++operator++()operator++(int)

我不完全确定您是否要使某些成员保持未初始化状态。你不应该那样做。而是重新考虑你的设计。如果在某些地方做得比你需要的多,那么这就是设计的味道。很可能是为一个班级做了太多的事情(参见单一责任原则)。MyClassMyClass

但是,如果您想要一个只能由一个函数访问的某种复制的构造函数,则可以这样做:

#include <iostream>

struct MyClass;

void foo(MyClass&);

struct Proxy {
    MyClass& object;
private:
    Proxy(MyClass& object) : object(object) {}
    friend void foo(MyClass&);
};

struct MyClass  {
    MyClass() = default;
    MyClass(const Proxy& p) {} // implement special copy here
    MyClass(const MyClass&) = default; // copy constructor
};

void foo(MyClass& a) {
    MyClass b = Proxy(a);
}

int main() {
    MyClass a;
    MyClass b = Proxy(a); // fails to compile
}

请注意如何仅访问而不是访问可能具有的任何私有部分。fooMyClass(const Proxy& p)MyClass