0

I have this class hierarchy where I'm trying to add operator= :

class A
{
public:
    virtual void someFunction() = 0;
    virtual A& operator=(const A&) = 0;
};

class B : public A
{
public:
    void someFunction() {
        //implementation
    }
    A& operator=(const A& o)
    {
        *ptr = *o.ptr;
        return *this;
    }

private:
    A* ptr;
};

class C : public A
{
public:
    void someFunction() {
        //implementation
    }
    A& operator=(const A& o)
    {
        data = o.data;
        return *this;
    }

private:
    int data;  //NOTE: different members that needs to be copied in the operator
};

I understand why this doesn't work. I have a private member in B (that needs to be there) and a function A&operator=(const A&) that needs to be overwritten. Problem is that o is of type A and doesn't have the pointer ptr.

I've tried to dynamic_cast o to type B, but

  1. that wont work since it's constant,
  2. It seems unsafe (if rhs is of type C)

Same issue for class C. Is there some cleaver work-around?

Clarification of why I need it this way:

class superClass
{
public:
  superClass& operator=(const superClass& o)
  {
    *some_A_type = *o.some_A_type;
  }
private:
  A* some_A_type;
};

essentially, what I want is an operator= for superClass. I'm not sure where or how to fix it.

Johan Hjalmarsson
  • 3,433
  • 4
  • 39
  • 64
  • Why is the assignment operator in `A` in the first place? I'd start with defending *that* decision first. – WhozCraig Jun 12 '14 at 08:11
  • Because of how it's later used. I have a class X that must be able to store any type of A-child and perform operations on them. – Johan Hjalmarsson Jun 12 '14 at 08:12
  • 3
    Assuming for a moment that made sense, what do you *expect* to happen when you `B b; C c; b = c;` ? Storing a reference to an `A` derivation needs no assignment operator. It needs references and/or pointers to the real object, otherwise you're eventually staring down the throat of [a slicing problem](http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c) – WhozCraig Jun 12 '14 at 08:14
  • That's why I said that dynamic_cast seems like a bad option. I want to be able to do `B b1, b2; C c1, c2; b1 = b2; c1 = c2;` but not `b1 = c1;` – Johan Hjalmarsson Jun 12 '14 at 08:16
  • 1
    Then you implement proper assignment for each class, leave it out of A, and use pointers (ideally smart ones) or references for your polymorphism. – WhozCraig Jun 12 '14 at 08:17
  • 1
    Maybe you find an answer [here](http://stackoverflow.com/questions/669818/virtual-assignment-operator-c/669894#669894). Especially Brian R. Bondy's answer looks quite promising. – TobiMcNamobi Jun 12 '14 at 08:21
  • @WhozCraig I updated the question to include my other class. Could you please explain where/how I fix it? – Johan Hjalmarsson Jun 12 '14 at 08:25

2 Answers2

0

Found in similiar question sugested by TobiMcNamobi:

class B : public A
{
public:
  virtual A& operator=(const A& p)
  {
    *ptr = *o.ptr;
    return *this;
  }


  virtual B& operator=(const B& p)
  {
    //throw exception
  }

};
Community
  • 1
  • 1
Johan Hjalmarsson
  • 3,433
  • 4
  • 39
  • 64
  • But you'll have to add methods for each descendant of A and in each descendant you'll have only one variant that doesn't throw exception. And finally everything (which method to call) will be decided in compile-time anyway. I.e. if you work in terms of `A` everywhere only `operator=(const A&)` will be called. – ony Jun 12 '14 at 09:20
0

You should re-consider your initial design of classes.

Also you should understand:

  • operator polymorphism (a + b works for both std::string and int)
  • data type cannot be polymorph by itself because memory layout should be defined
  • what is abstract class and/or interface
  • maybe static polymorphism would be useful too

First try to imagine what implication of having assignment between any object of any class within class A. I.e. to store object of B in object of C we should change state of object from C in that way that its characteristics will be equivalent object to original object from B. This can be achieved either by having common memory layout (i.e. all descendants store same data) between all descendants of A or by exposing same behavior in some other way like referencing to original object.

Note that behavior of virtual void someFunction() should also be copied.

Let's try to pull out maximum out of your sample:

// our interface
struct A {
    virtual void someFunction() = 0;
    // no polymorphic assignment
};  

struct B : A {
    void someFunction();
    B &operator=(const A &o)
    { ptr = &o; return *this; }
private:
    A *ptr;
}   

struct C : A {
    void someFunction();
    A &operator=(const C &o)
    { data = o.data; return *this; }
private:
    int data;
};

C c, c2;
B b;
A &a = c;
b = c; // ok
b = a; // ok
c = c2; // ok
c = b; // wrong

Or if you still want polymorphic assignment:

// interface. descendants are responsible for maintaining LSP
struct A {
    void someFunction()
    { cout << data(); }
    virtual int getData() const = 0;
    // assignment should result in copying chars and making getData() to behave like in original object
    virtual A &operator=(const A &o) = 0; 
};  

struct B : A {
    int getData() const { return ptr->getData(); }
    A &operator=(const A &o)
    { ptr = &o; return *this; }
private:
    const A *ptr;
};

struct C : A {
    int getData() const { return data; }
    A &operator=(const A &o)
    { data = o.getData(); return *this; }
private:
    int data;
};

P.S. Last variant probably unwanted in real world.

ony
  • 12,457
  • 1
  • 33
  • 41