开发者

How to implement copy operator for such C++ structure?

So having

struct ResultStructure
{
    ResultStructure(const ResultStructure& other)
    {
        // copy code in here ? using memcpy ? how???  
    }
    ResultStructure& operator=(const ResultStructure& other)
    {
        if (this != &other) {
            // copy code in here ?
        }
        return *this
    }
    int length;
    char* ptr;
};

How to implement "copy constructor" and "assignment operator"? (sorry - I am C++ nube)

Update: sbi and others ask - why do I want to manually deal with raw memory? My answer is simple - In a students project I am part of now we use lots of C library's such as for example OpenCV OpenAL and FFmpeg and there are more to come. Currently using C++ we try to create a graph based direct show like cross platform library that would be helpful in live video broadcasting and processing. Our graph elements currently use char* and int pairs for data exchange. To cast data to subscribed elements we use raw memcpy now. I want to go further and make it possible for us to make our graph elements base C++ template. So that one graph element would be capable of of sharing current graph element data with other Graph elements and that data it shares would be a structure that would contain not one char* one int but any number of data fields and 开发者_运维问答nearly any elements inside. So that is why I need to understand how to create a basic C++ structure that implements "copy constructor" and "assignment operator" for me to be capable to use new for us data casting algorithms like

void CastData(T item){
    for(size_t i = 0 ; i < FuncVec.size(); i++){
        T dataCopy = item;
        FuncVec[i](dataCopy);
    }
}

instead of currently used

void CastData(char * data, int length){
    for(size_t i = 0 ; i < FuncVec.size(); i++){
        char* dataCopy = new char[length];
        memcpy(dataCopy, data, length);
        FuncVec[i](dataCopy, length);
                    delete[] dataCopy;
    }
}


You might want to explain why you want to manually deal with raw memory. I haven't done this in a long time, it's what std::string and std::vector where designed for:

struct ResultStructure
{
    // nothing else needed

    std::string data; // or std::vector<char>
};

But if you really need to do this the hard way (is this homework?), then be advised that it is, at first, surprisingly hard to get this right. For example, a naive implementation of the assignment operator might be like this:

// DON'T TRY THIS AT HOME!!
ResultStructure& ResultStructure::operator=(const ResultStructure& rhs)
{
  delete[] ptr;                               // free old ressource
  ptr = new char[rhs.length];                 // allocate new resourse
  std::copy(rhs.ptr, rhs.ptr+rhs.length, ptr; // copy data
  length = rhs.length;
}

If someone accidentally assigns an object to itself (which might happen if all you have is two references and you don't suspect them to refer to the same object), then this will fail fatally.
Also, what if new throws an exception? (It might throw std::bad_alloc if memory is exhausted.) Then we have already deleted the old data and have not allocated new data. The pointer, however, still points at where the old data used to be (actually, I think this is implementation-defined, but I have yet to see an implementation that changes a ptr upon deletion), and the class' destructor (you knew that class would need a destructor, right?) would then attempt to delete a piece of data at an address where no data is allocated. That's Undefined Behavior. The best you can hope for is that it crashes immediately.

The easiest way to do this is to employ the Copy-And-Swap idiom:

struct ResultStructure
{
    ResultStructure(const ResultStructure& other)
     : ptr(new char[rhs.length]), length(rhs.length)
    {
        std::copy(rhs.ptr, rhs.ptr+rhs.length, ptr);
    }

    ~ResultStructure() // your class needs this
    {
      delete[] ptr;
    }

    ResultStructure& operator=(ResultStructure rhs) // note: passed by copy
    {
        this->swap(rhs);
        return *this
    }

    void swap(const ResultStruct& rhs)
    {
      using std::swap;
      swap(length, rhs.length); 
      swap(ptr, rhs.ptr); 
    }

    std::size_t length;
    char* ptr;
};

Note that I have added a destructor, changed the assignment operator to pass the argument per copy (we need the copy constructor invoked to allocate memory), and added a swap member function. Per convention a swap() function never throws and is fast, preferably O(1).

I know that GMan's discussion of the Copy-And-Swap idiom is exhaustive enough to be and exhausting while mine is probably too terse for you, and you will likely not understand a lot of it at first, but try to persevere and to understand as much as possible.


If you use std::string, instead of char*, you would not even need to write operator= or copy-constructor. The compiler generated code would do your job very well.

But as a general solution (for some other scenario), use copy-and-swap idiom:

  • Copy-and-Swap Idiom
  • What is the copy-and-swap idiom?

Exceptional C++ by Herb Sutter has described these in great detail. I would recommend you to read items from this book. For the time being, you can read this article online:

  • Exception-Safe Generic Containers


The easy solution is to use a std::string instead of char* member.

Then the compiler-generated copy constructor and copy assignment operator just work.

As a rule, and especially as a novice, don't have raw pointer members.

Cheers & hth.,


As has been said, and as was recommending in the question this emanated from, you should probably reuse an existing container. At least the code would be right :)

For educational purposes though, let's examine this structure:

class Result
{
public:

private:
  size_t length; // can't really be negative, right ?
  char* data;
};

Here, we need explicit memory management. This implies, notably, following the Rule Of Three (say thanks to Fred)

Let's begin with actually building our object:

Result::Result(): length(0), data(0) {}

Result::Result(size_t l, char* d): length(0), data(0)
{
  if (!d) { return; }

  data = new char[l]; // this may throw, so we do it first

  length = l;
  memcpy(data, d, l);
}

Now we can implement the traditional operators:

// Swap
void Result::swap(Result& other)
{
  using std::swap;

  swap(length, other.length);
  swap(data, other.data);
}

// Copy Constructor
Result::Result(Result const& other): length(0), data(0)
{
  if (!other.length) { return; }

  data = new char[other.length];
  length = other.length;
  mempcy(data, other.data, length);
}

// Assignemt Operator
Result& Result::operator=(Result other)
{
  this->swap(other);
  return *this;
}

// !IMPORTANT!
// Destructor
Result::~Result()
{
  delete[] data; // use the array form of delete
                 // no need to test for nullity, it's handled
}


this is std::vector<char>'s job - or is this homework?

the vector would replace both length and the allocation behind ptr. the vector is the c++ idiom, and you'll not make friends with other c++ devs if you implement your classes like you've described. of course, there are corner cases, but standard containers such as vector are the default.

the vector knows how to copy chars as well as itself, and the implementations are optimized and tested.

here's how to explicitly implement copy ctor/assign using a vector:

struct ResultStructure {
    ResultStructure(const ResultStructure& other) : d_chars(other.d_chars) {
    }

    ResultStructure& operator=(const ResultStructure& other) {
        if (this != &other) {
            this->d_chars = other.d_chars;
        }
        return *this;
    }
    std::vector<char> d_chars;
};


I think this should do the work:

struct ResultStructure
{
    ResultStructure(const ResultStructure& other);
    ResultStructure& operator=(const ResultStructure& other);

    int length;
    char* ptr;
};

ResultStructure::ResultStructure(const ResultStructure& other):length(other.length)
{
    ptr = (char*)malloc(length);
    memcpy(ptr, other.ptr, length);
}

ResultStructure& ResultStructure::operator=(const ResultStructure& other)
{
    length = other.length;
    ptr = (char*)malloc(length);
    memcpy(ptr, other.ptr, length);
    return *this;
}

Please remember about freeing ptr in destructor. What is stored under ptr? If text, why not to use std::string? Anyway you can use std::vector. The constructors will be much easier then...


How is the memory to which ptr points allocated?

if using new, allocate with new, set length and then copy

other.length = length;
other.ptr = new char[length];
memcpy( other.ptr, ptr, length );

If you're allocating the memory with malloc, substitute a call to malloc in place of the call to new. If you're using some other memory scheme, figure it out. :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜