2

我想进一步讨论这个话题。

假设我有类似的东西:

class MyClass
{
public: 

MyClass(int N)
{ 
    data_ptr = new float[N];
};

float* dat_ptr;    

// ... clever operator definition here ...
};

所以我希望能够简单地写:

MyClass a(4);
MyClass b(4);
MyClass c(4);

// modify b.data_ptr and c.data_ptr ....
// Use "clever operator"
a = b + c;

操作员将在哪里执行 a.data_ptr[i] = b.data_ptr[i] + c.data_ptr[i] for i=0:(N-1) ...

因此,不会创建额外的数据副本,并且我们巧妙地使用了预分配的缓冲区。

这可能吗?如果是这样,请向我提供有关如何完成的一些见解。

谢谢!

4

3 回答 3

1

如果您使用 C++11 中的移动语义,则可以。

class MyClass
{
public: 

    MyClass(int N)
    { 
        data_ptr = new float[N];
        n = N;
    }

    MyClass(MyClass && rhs)
    {
        data_ptr = rhs.data_ptr;
        n = rhs.n;

        rhs.data_ptr = nullptr;
    }

    // dtor, copy-ctor etc.

    int n;
    float * dat_ptr;    
};

MyClass operator + (const MyClass & left, const MyClass & right)
{
    MyClass result(left.n);

    // Implement addition
}

// Note: no error-checking

这样创建一个临时对象,但不会不必要地复制内部数据。

阅读有关移动语义的更多信息。

于 2013-05-29T09:38:07.950 回答
1

这不可能; 在分配 a 之前,调用 operator + (b, c) 会创建一个临时对象;此运算符应返回创建的实例,然后将其分配给 a; 创建的实例总是由 b + c 创建的。

不过,可以将 += 定义为成员运算符并说:

b += c;

这将修改 b 的值而不创建额外的副本。

编辑:我重新考虑了:)

您绝对可以通过将操作抽象为惰性求值对象来做到这一点。

这是一个例子:

class MyClass; // fwd. declaration of your class

struct LazySum
{
    LazySum(const MyClass& a, const MyClass& b)
    : x(a), y(b) {}

    float operator[](int i) { return x[i] + y[i]; }

    const MyClass& x;
    const MyClass& y;
};

class MyClass
{
public: 
    MyClass(int N)
    { 
        data_ptr = new float[n = N];
    };

    int n;           // this shouldn't be public
    float* dat_ptr;  // nor this, but I went with your code

    // ... clever operator definition here ...
    MyClass& operator=(const LazySum& terms)
    {
        // ignore case when n != x.n or n != y.n
        // because not the point of the example
        // (and I'm lazy)

        // sum evaluation occurs here
        // with no new allocations
        for(int i = 0; i < n; ++i)
            data_ptr[i] = terms[i]; 
        return *this;
    }
};

LazySum operator=(const MyClass& x, const MyClass& y)
{
    return LazySum(x, y); // LazySum is a couple of references in size
}

void client_code_using_clever_op()
{
    MyClass a(4);
    MyClass b(4);
    MyClass c(4);

    // modify b.data_ptr and c.data_ptr ....
    // Use "clever operator"
    a = b + c; // actual sum performed when operator = is executed
}

这个想法是存储术语,并对术语执行后期评估。

改进点:

  • 在 LazySum 的构造中注入一个函子,使其成为 LazyOp(函子将决定 op 是什么);根据它在 MyClass 上实现其他二元运算符。

  • 在 MyClass 中使用 RAII。

  • 当您需要在另一种类型(例如 some MyOtherClass)上实现惰性求值运算符时,请考虑将 LazyOp 实现为术语和仿函数类型的模板。

  • 如果没有一些额外的工作,这不支持更复杂的表达式:

    我的类 a(4)、b(4)、c(4)、d(4);

    d = (a + b) + c; // 错误

    这个例子不起作用,因为它需要一个operator+(const LazySum&, const MyClass&);;

于 2013-05-29T09:30:46.913 回答
0

正如 Spook 解释的那样,是的,这是可能的。只是为了好玩,我写了一个完整的例子,你可以编译和运行。如果要创建副本,您将在输出中收到一条消息。我在 Visual Studio 2012 中尝试了这个示例并且运行良好。

class MyClass
{
private:
    float *data_ptr;
    std::size_t size;

public:
    MyClass(std::size_t N = 0) :
        size(N),
        data_ptr(N ? new float[N]() : nullptr)
    {}

    MyClass(const MyClass& other) :
        size(other.size),
        data_ptr(other.size ? new float[other.size]() : nullptr)
    {
        std::copy(other.data_ptr, other.data_ptr + size, data_ptr);
        std::cout << "Copy!" << std::endl;
    }

    MyClass(MyClass&& other) 
    {
        size = 0;
        data_ptr = nullptr;

        swap(*this, other);
    }

    ~MyClass()
    {
        delete[] data_ptr;
    }

    MyClass& operator=(MyClass other)
    {
        swap(*this, other);

        return *this;
    }

    friend MyClass operator+(MyClass& first, MyClass& second)
    {
        MyClass result(std::min(first.size, second.size));

        for (std::size_t i=0; i < result.size; i++) {
            result.data_ptr[i] = first.data_ptr[i] + second.data_ptr[i];
        }

        return result;
    }

    friend void swap(MyClass& first, MyClass& second)
    {
        std::swap(first.size, second.size);
        std::swap(first.data_ptr, second.data_ptr);
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    MyClass a(5);
    MyClass b(5);
    MyClass c(5);

    a = b + c; //this should not produce an extra copy

    return 0;
}
于 2013-05-29T10:27:57.470 回答