0

First things first, I have the below class A which at the same time have a nested class B:

// A.h
class A {
public:
    class B;

    A();
    A(const A& a); // Copy constructor

    B& operator[](const unsigned int& a) const;
    A operator+(const A& a) const;

    /*...*/

    ~A();
private:
    /*...*/
    unsigned int size;
    B* b;
};

and I'm trying to use the overloaded + operator to "add" two A objects by adding the content of both b members of said objects, and assign the result to a third A object.

  • b is a dinamically allocated array of B objects.

  • B is a pretty basic class with just some unsigned int

here's the main function:

// main.cpp
int main(int argc, char** argv) {
    A a1, a2, result;

    a1.read(argv[1]); // Initialize b member with content read from a file
    a2.read(argv[2]); // Initialize b member with content read from a file

    result = a1 + a2; // Error [3]

    getchar();

    return 0;
}

The problem is that when trying to make the sum I get what I think are memory errors like: HEAP[main.exe]: Invalid address specified to RtlValidateHeap( 00A50000, 00A59938 )

here's the implementation of class A:

// A.cpp
A::A() : /*...*/, size(0), b(nullptr) {}

// Copy constructor
A::A(const A& a) : /*...*/, size(a.size), b(nullptr) {
    b = new B[size]; // [1]

    for (unsigned int i = 0; i < size; i++) {
        (*this)[i] = a[i];
    }
}

A::B& A::operator[](const unsigned int& i) const {
    return b[i];
}

A A::operator+(const A& a) const {
    if (size != a.size) {
        exit(1); // Size must be the same on both operands
    }

    A tmp(*this); // Call to copy constructor

    for (unsigned int i = 0; i < a.size; i++) {
        tmp[i] += a[i];
    }

    return tmp; // Call to copy constructor [2]
}

A::~A() {
    if (b != nullptr) {
        delete[] b;
    }
}

and of class B:

// B.h
class A::B {
public:
    B();
    B(unsigned char, unsigned char, unsigned char);

    B& operator+=(const B& b);
private:
    unsigned int a, b, c, d;
};

// B.cpp
A::B::B() : a(0), b(0), c(0), d(0) {}
A::B::B(unsigned char _a, unsigned char _b, unsigned char _c) {
    /*...*/
}

A::B& A::B::operator+=(const B& b) {
    /*...*/

    return *this;
}

I'm using Visual Studio by the way, and when debugging I've observed:

  1. b member of result points to the same address pointed by b in [1] when the copy constructor is called by the return statement in [2], so far so good

  2. Until the return in [2] the content of b is all right, something like: 0x00669968 00 00 ff 00 00 ff 00 00 ..ÿ..ÿ..

  3. After [3] the content of b in [1] and therefore the content of b member of result object becomes something like: 0x00669968 dd dd dd dd dd dd dd dd ÝÝÝÝÝÝÝÝ, I'm guessing is garbage

Note: all include directives and irrelevant sections of code have been ommited

I've been like two days shaking my head trying to figure out what's wrong with no luck so any help is greatly appreciated, thanks in advance.

user0370730
  • 31
  • 1
  • 7
  • 1
    I problem that I can see, it is in function `A A::operator+(const A& a) const` when `a` and `this` have different sizes you will read invalid addresses. You need to put a restriction that the size the both A objects must be equals. – chema989 Jun 20 '16 at 22:44
  • You can add the definition of `class B`. – chema989 Jun 20 '16 at 22:55
  • @chema989 Thanks for your help, every try that I've made have been with objects of the same size though, anyway I've updated the code with that restriction to reflect what you said and also added the definition of class `B` – user0370730 Jun 20 '16 at 23:21
  • You should be validating `this->size != a.size` *before* creating the `tmp` object. And `img.size` should be `a.size`. But more importantly, how does `A::read()` manipulate the `b` array? You are probably corrupting the array before `operator+` is being called. Also, on a separate note, `operator+` should be implemented as `operator+=` inside of `A` itself (just like you do with `B::operator+=`, and then you should define a separate non-member `operator+` to add two `A`s together. – Remy Lebeau Jun 21 '16 at 00:25
  • Relevant reading: [What is the Rule of Three?](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) – M.M Jun 21 '16 at 02:23
  • Consider using `std::unique_ptr b;` instead of `B *b;` - then the compiler would have diagnosed the problem for you – M.M Jun 21 '16 at 02:24
  • Be aware that your design allows the members of `b` to be modified even for a const `A`. – M.M Jun 21 '16 at 02:25
  • @RemyLebeau Thanks for the tips, I've updated the code to check the size of both objects before creating `tmp` – user0370730 Jun 21 '16 at 18:33
  • @M.M Thank you, about the use of `std::unique_ptr` I've trying to avoid the use of smart pointers as I'm just starting to learn c++, I'll probably start using them from now on – user0370730 Jun 21 '16 at 18:39

1 Answers1

1

I check your code and the problem is that you need a custom copy assignment for class A. In your main you have A a1, a2, result;, the default constructor is called for the 3 objects. Then, in the line result = a1 + a2;, the default copy assignment is called.

When you have pointers in your class and allocate the memory using new then you have to worry about copy constructor and copy assignment. Check this post and the rule of three.


I propose you the next code:

class A {
    class B {
        unsigned a, b, c, d;

    public:
        B() : a(0), b(0), c(0), d(0) { }

        B(unsigned char a_, unsigned char b_, unsigned char c_) : a(a_), b(b_), c(c_), d(0) { }

        // Copy constructor.
        B& operator=(const B& b_) {
            a = b_.a;
            b = b_.b;
            c = b_.c;
            d = b_.d;
            return *this;
        }

        B& operator+=(const B& b_) {
            a += b_.a;
            b += b_.b;
            c += b_.c;
            d += b_.d;
            return *this;
        }
    };

    unsigned size;
    B* b;

public:
    A() : size(0) { }

    // Copy constructor.
    A(const A& a) : size(a.size) {
        b = new B[size];
        for (unsigned i = 0; i < size; ++i) {
            b[i] = a[i];
        }
    }

    // Copy assigment
    A& operator=(const A& a) {
        clear();
        b = new B[size];
        for (unsigned i = 0; i < size; ++i) {
            b[i] = a[i];
        }
        return *this;
    }

    B& operator[](unsigned pos) const {
        if (pos > size) {
            throw std::out_of_range("Out of range");
        }
        return b[pos];
    }

    A operator+(const A& a) const {
        A tmp = *this;
        if (size != a.size) {
            throw std::out_of_range("Diferent sizes");
        }
        for (unsigned i = 0; i < a.size; ++i) {
            tmp[i] += a[i];
        }
        return tmp;
    }

    void read(const char* file) {
        clear();
        size = size_;
        b = new B[size];
        /*
         * Read your file and update b.
         */
    }

    void clear() {
        if (size) {
            delete[] b;
            size = 0;
        }
    }

    ~A() {
        clear();
    }
};
Community
  • 1
  • 1
chema989
  • 3,962
  • 2
  • 20
  • 33
  • 1
    Agree that the problem is that `A::operator=` is missing; however OP indicated that they want `B` to be hidden from the header – M.M Jun 21 '16 at 02:23
  • Perfect, definitely I was missing the copy assignment operator, thanks for the tips also! – user0370730 Jun 21 '16 at 19:02