1

I have the following code.

class Wave {

int m_length;
data_type * m_data;

public:

    Wave(){
        blah...blah...blah
        m_data = NULL;
        m_length = 0;
        cout << "Wave " << this << " created on " << m_data << " with m_length " <<  m_length << endl;
    }

    Wave(int len, data_type data){
        blah...blah...blah
        if (len) {
            m_length = len;
            m_data = new data_type [m_length];
        } else {
            m_length = 0;
            m_data = NULL;
        }
        cout << "Wave " << this << " created on " << m_data << " with m_length " <<  m_length << endl;
    }



    ~Wave() 
    {
    cout << "Wave " << this << " destructor  on " << m_data << " started ";
        if (m_length) delete[] m_data;
    cout << "and finished " << endl;
    };        





    Wave & operator+= (const Wave wave){
    cout << __FUNCTION__ << ":" << __LINE__ << " m_length " << m_length << endl;
    if (NULL != m_data){
        data_type * tBuf = new data_type [m_length + wave.Length()];
        copy (wave.begin(),wave.end(), copy (begin(),end(),iterator(tBuf)));
        cout << "Wave " << this << " data on " << m_data << " moved onto " << tBuf;
        delete[] m_data;
        m_data = tBuf;
        cout << " and deleted" << endl;
    } else {
        m_data = new data_type [wave.Length()];
        copy (wave.begin(), wave.end(), begin());
        cout << "Wave " << this << " data created on " << m_data << " of length " <<  wave.Length() << endl;
    }
    m_length += wave.Length();
    cout << __FUNCTION__ << ":" << __LINE__ << " m_length " << m_length << endl;
    return *this;
    };

}


main(){

blah..blah..blah

Wave sample;

for (......) {

    cout << pulseNum << "-th High part: " << pulse->first << endl;
    Wave tSample(x,y);
    blah.blah.blah

    sample += tSample;

    cout << endl << pulseNum++ << "-th Low part: " << pulse->second << endl;
    tSample = Wave(a,b);
    blah.blah.blah

    sample += tSample;
}

}

Below is a log of execution this code

Wave 0x28fe34 created on 0 with m_length 0
0-th High part: 220
Wave 0x28fe54 created on 0xc60f00 with m_length 207
operator+=:211 m_length 0
Wave 0x28fe34 data created on 0xc610a8 of length 207
operator+=:230 m_length 207
Wave 0x28fe9c destructor  on 0xc60f00 started and finished

0-th Low part: 320
Wave 0x28febc created on 0xc61250 with m_length 301
Wave 0x28febc destructor  on 0xc61250 started and finished
operator+=:211 m_length 207
Wave 0x28fe34 data on 0xc610a8 moved to 0xc61250 and deleted 
operator+=:230 m_length 508
Wave 0x28fee0 destructor  on 0xc61250 started and finished

Wave 0x28fe54 destructor  on 0xc61250 started and finished

Most strange thing for me is that destructor is called more times than contructor. Moreover, it was called for objects never costructed before but for the same data address.
How can it be?

OlegG
  • 975
  • 3
  • 10
  • 30

2 Answers2

8

Wave has a compiler-generated copy constructor, which you see no output for.

This copy constructor is called for example to construct the object which is the parameter of Wave & operator+= (const Wave wave).

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 2
    OP: You might consider declaring `Wave & operator += (const Wave& wave)` which will save a lot of copy operations. – yo' Oct 18 '12 at 11:36
  • Steeve@ So, what happenes when two calls for destructor deletes the same data buffer? Like that: Wave 0x28fee0 destructor on 0xc61250 started and finished Wave 0x28fe54 destructor on 0xc61250 started and finished – OlegG Oct 18 '12 at 11:39
  • 1
    @OlegG: behavior is undefined when you double-free the buffer (which is another way of saying, there's a bug in your code). Since both the original object and the copy have a pointer to the same buffer, they'll both free it in their destructors. – Steve Jessop Oct 18 '12 at 11:47
2

If you want to simplify your code, just define a std::vector<data_type> m_data data member, instead of using raw pointers (data_type * m_data).

In this way, the compiler will automatically generate proper copy constructor, copy operator= (and also move semantics for C++11-compliant compilers) and destructor for you class (the automatically generated copy constructor, copy operator= and destructor will operate member-wise, e.g. the automatically generated copy constructor will call copy constructors for each data members of your class).

To allocate data, instead of:

m_data = new data_type [m_length];

use:

m_data.resize(m_length);

You can also get rid of m_length data member, since the std::vector knows its own size (you can access it via std::vector::size() method).

Note that std::vector stores all its elements in a single continuous memory area (like new[]); you can access the beginning of that area using &m_data[0].

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • 1
    Mr.C64@ The requirements is that all managed objects of `data_type` must be located in continious memory area which will be passed to another procedure then. Does vector allow me to do this? – OlegG Oct 18 '12 at 11:45
  • 1
    @OlegG: yes, `vector` stores all its elements in a single continuous memory area. – Steve Jessop Oct 18 '12 at 11:48
  • @Steve And how can I pass address of this area to a procedure which require argument of `void *` type? – OlegG Oct 18 '12 at 11:54
  • @OlegG: Yes, see Steve Jessop's comment. – Mr.C64 Oct 18 '12 at 12:32
  • @OlegG: I edited the post adding a sentence to address your questions on memory continuity and addressing the beginning of that memory area. – Mr.C64 Oct 18 '12 at 12:36