Assignment through base class reference

Discussion in 'C++' started by anongroupaccount@googlemail.com, Jan 20, 2006.

  1. Guest

    What measures should be taken to avoid this sort of thing?

    class Base
    {
    };

    class Derived1 : public Base
    {
    private:
    int i, j, k;
    };

    class Derived2 : public Base
    {
    private:
    double l, m, n;
    };

    void BaseAssign(Base& lhs, Base& rhs)
    {
    lhs = rhs;
    }

    int main(int argc, char *argv[])
    {
    Derived1 d1;
    Derived2 d2;

    BaseAssign(d1, d2);
    }

    // End code snippet

    The only way I can see around preventing this sort of thing is
    declaring the assignment operators and copy constructors of non-leaf
    classes protected, and providing clone/create methods instead of
    letting the operators/constructors be used.

    Do people generally just not worry about this sort of thing? I'm
    absolutely paranoid about stuff like this.
    , Jan 20, 2006
    #1
    1. Advertising

  2. wrote:
    > What measures should be taken to avoid this sort of thing?


    WHY?

    > class Base
    > {
    > };
    >
    > class Derived1 : public Base
    > {
    > private:
    > int i, j, k;
    > };
    >
    > class Derived2 : public Base
    > {
    > private:
    > double l, m, n;
    > };
    >
    > void BaseAssign(Base& lhs, Base& rhs)
    > {
    > lhs = rhs;
    > }
    >
    > int main(int argc, char *argv[])
    > {
    > Derived1 d1;
    > Derived2 d2;
    >
    > BaseAssign(d1, d2);
    > }
    >
    > // End code snippet
    >
    > The only way I can see around preventing this sort of thing is


    Why prevent this sort of thing? Do you experience any problem? Please
    elaborate.

    > declaring the assignment operators and copy constructors of non-leaf
    > classes protected, and providing clone/create methods instead of
    > letting the operators/constructors be used.
    >
    > Do people generally just not worry about this sort of thing? I'm
    > absolutely paranoid about stuff like this.


    Well, could you share? Maybe I need to become paranoid about it as
    well...

    V
    Victor Bazarov, Jan 20, 2006
    #2
    1. Advertising

  3. Guest

    Victor Bazarov wrote:
    > wrote:
    > > What measures should be taken to avoid this sort of thing?

    >
    > WHY?
    >
    > > class Base
    > > {
    > > };
    > >
    > > class Derived1 : public Base
    > > {
    > > private:
    > > int i, j, k;
    > > };
    > >
    > > class Derived2 : public Base
    > > {
    > > private:
    > > double l, m, n;
    > > };
    > >
    > > void BaseAssign(Base& lhs, Base& rhs)
    > > {
    > > lhs = rhs;
    > > }
    > >
    > > int main(int argc, char *argv[])
    > > {
    > > Derived1 d1;
    > > Derived2 d2;
    > >
    > > BaseAssign(d1, d2);
    > > }
    > >
    > > // End code snippet
    > >
    > > The only way I can see around preventing this sort of thing is

    >
    > Why prevent this sort of thing? Do you experience any problem? Please
    > elaborate.
    >
    > > declaring the assignment operators and copy constructors of non-leaf
    > > classes protected, and providing clone/create methods instead of
    > > letting the operators/constructors be used.
    > >
    > > Do people generally just not worry about this sort of thing? I'm
    > > absolutely paranoid about stuff like this.

    >
    > Well, could you share? Maybe I need to become paranoid about it as
    > well...
    >
    > V


    The classes must be getting sliced... I mean, how can a Derived2 be
    assigned to a Derived1?
    , Jan 20, 2006
    #3
  4. wrote:
    > Victor Bazarov wrote:
    >
    >> wrote:
    >>
    >>>What measures should be taken to avoid this sort of thing?

    >>
    >>WHY?
    >>
    >>
    >>>class Base
    >>>{
    >>>};
    >>>
    >>>class Derived1 : public Base
    >>>{
    >>> private:
    >>> int i, j, k;
    >>>};
    >>>
    >>>class Derived2 : public Base
    >>>{
    >>> private:
    >>> double l, m, n;
    >>>};
    >>>
    >>>void BaseAssign(Base& lhs, Base& rhs)
    >>>{
    >>> lhs = rhs;
    >>>}
    >>>
    >>>int main(int argc, char *argv[])
    >>>{
    >>> Derived1 d1;
    >>> Derived2 d2;
    >>>
    >>> BaseAssign(d1, d2);
    >>>}
    >>>
    >>>// End code snippet
    >>>
    >>>The only way I can see around preventing this sort of thing is

    >>
    >>Why prevent this sort of thing? Do you experience any problem? Please
    >>elaborate.
    >>
    >>
    >>>declaring the assignment operators and copy constructors of non-leaf
    >>>classes protected, and providing clone/create methods instead of
    >>>letting the operators/constructors be used.
    >>>
    >>>Do people generally just not worry about this sort of thing? I'm
    >>>absolutely paranoid about stuff like this.

    >>
    >>Well, could you share? Maybe I need to become paranoid about it as
    >>well...
    >>
    >>V

    >
    >
    > The classes must be getting sliced... I mean, how can a Derived2 be
    > assigned to a Derived1?
    >


    Where did you get that idea, about slicing? The Base subobject of 'd1' is
    simply made _the_same_ as the Base subobject of 'd2'. At least that's
    what it means [to me] *semantically*. Nothing more and nothing less.

    Whatever 'd1' has _above_and_beyond_ its 'Base' subobject, is kept intact.
    Whatever 'd2' has _above_and_beyond_ its 'Base' subobject, is not used at
    all in that operation. I suppose the following is even scarier to you:

    class Derived3 : public Derived2 {};
    class Derived4 : public Derived3 { std::string name; };

    ...
    Derived4 d4;
    BaseAssign(d1, d4);

    It shouldn't be. Relax. Take a deep breath. Nothing *bad* is happening
    here. If your problem domain prohibits that (for whatever reason, you did
    not say anything about the problem domain), you _could_ disable it by
    making 'Base' protected base class, but then LSP cannot be applied...
    Maybe that's what you want... Speak up, then.

    V
    Victor Bazarov, Jan 20, 2006
    #4
  5. Guest

    Victor Bazarov wrote:
    > wrote:
    > > Victor Bazarov wrote:
    > >
    > >> wrote:
    > >>
    > >>>What measures should be taken to avoid this sort of thing?
    > >>
    > >>WHY?
    > >>
    > >>
    > >>>class Base
    > >>>{
    > >>>};
    > >>>
    > >>>class Derived1 : public Base
    > >>>{
    > >>> private:
    > >>> int i, j, k;
    > >>>};

    > >
    > >>>class Derived2 : public Base
    > >>>{
    > >>> private:
    > >>> double l, m, n;
    > >>>};
    > >>>
    > >>>void BaseAssign(Base& lhs, Base& rhs)
    > >>>{
    > >>> lhs = rhs;
    > >>>}
    > >>>
    > >>>int main(int argc, char *argv[])
    > >>>{
    > >>> Derived1 d1;
    > >>> Derived2 d2;
    > >>>
    > >>> BaseAssign(d1, d2);
    > >>>}
    > >>>
    > >>>// End code snippet
    > >>>
    > >>>The only way I can see around preventing this sort of thing is
    > >>
    > >>Why prevent this sort of thing? Do you experience any problem? Please
    > >>elaborate.
    > >>
    > >>
    > >>>declaring the assignment operators and copy constructors of non-leaf
    > >>>classes protected, and providing clone/create methods instead of
    > >>>letting the operators/constructors be used.
    > >>>
    > >>>Do people generally just not worry about this sort of thing? I'm
    > >>>absolutely paranoid about stuff like this.
    > >>
    > >>Well, could you share? Maybe I need to become paranoid about it as
    > >>well...
    > >>
    > >>V

    > >
    > >
    > > The classes must be getting sliced... I mean, how can a Derived2 be
    > > assigned to a Derived1?
    > >

    >
    > Where did you get that idea, about slicing? The Base subobject of 'd1' is
    > simply made _the_same_ as the Base subobject of 'd2'. At least that's
    > what it means [to me] *semantically*. Nothing more and nothing less.
    >
    > Whatever 'd1' has _above_and_beyond_ its 'Base' subobject, is kept intact.
    > Whatever 'd2' has _above_and_beyond_ its 'Base' subobject, is not used at
    > all in that operation. I suppose the following is even scarier to you:
    >
    > class Derived3 : public Derived2 {};
    > class Derived4 : public Derived3 { std::string name; };
    >
    > ...
    > Derived4 d4;
    > BaseAssign(d1, d4);
    >
    > It shouldn't be. Relax. Take a deep breath. Nothing *bad* is happening
    > here. If your problem domain prohibits that (for whatever reason, you did
    > not say anything about the problem domain), you _could_ disable it by
    > making 'Base' protected base class, but then LSP cannot be applied...
    > Maybe that's what you want... Speak up, then.
    >
    > V


    You seem to get angry when people ask civil questions about things they
    don't understand. I feel like I'm going to be shouted at just for being
    inquisitive and trying to understand something properly.

    So assignment doesn't work "polymorphically"? This differs to C# (I
    wrote a similar test, and found that the object of type Derived1
    /becomes/ a Derived2 when assigned through a Base reference). I guess
    this is why boxing works in C# with the universal base class "object".
    , Jan 21, 2006
    #5
  6. wrote:
    > Victor Bazarov wrote:
    >> wrote:
    >>> Victor Bazarov wrote:
    >>>
    >>>> wrote:
    >>>>
    >>>>> What measures should be taken to avoid this sort of thing?
    >>>>
    >>>> WHY?
    >>>>
    >>>>
    >>>>> class Base
    >>>>> {
    >>>>> };
    >>>>>
    >>>>> class Derived1 : public Base
    >>>>> {
    >>>>> private:
    >>>>> int i, j, k;
    >>>>> };
    >>>
    >>>>> class Derived2 : public Base
    >>>>> {
    >>>>> private:
    >>>>> double l, m, n;
    >>>>> };
    >>>>>
    >>>>> void BaseAssign(Base& lhs, Base& rhs)
    >>>>> {
    >>>>> lhs = rhs;
    >>>>> }
    >>>>>
    >>>>> int main(int argc, char *argv[])
    >>>>> {
    >>>>> Derived1 d1;
    >>>>> Derived2 d2;
    >>>>>
    >>>>> BaseAssign(d1, d2);
    >>>>> }
    >>>>>
    >>>>> // End code snippet
    >>>>>
    >>>>> The only way I can see around preventing this sort of thing is
    >>>>
    >>>> Why prevent this sort of thing? Do you experience any problem?
    >>>> Please elaborate.
    >>>>
    >>>>
    >>>>> declaring the assignment operators and copy constructors of
    >>>>> non-leaf classes protected, and providing clone/create methods
    >>>>> instead of letting the operators/constructors be used.
    >>>>>
    >>>>> Do people generally just not worry about this sort of thing? I'm
    >>>>> absolutely paranoid about stuff like this.
    >>>>
    >>>> Well, could you share? Maybe I need to become paranoid about it as
    >>>> well...
    >>>>
    >>>> V
    >>>
    >>>
    >>> The classes must be getting sliced... I mean, how can a Derived2 be
    >>> assigned to a Derived1?
    >>>

    >>
    >> Where did you get that idea, about slicing? The Base subobject of
    >> 'd1' is simply made _the_same_ as the Base subobject of 'd2'. At
    >> least that's what it means [to me] *semantically*. Nothing more and
    >> nothing less.
    >>
    >> Whatever 'd1' has _above_and_beyond_ its 'Base' subobject, is kept
    >> intact. Whatever 'd2' has _above_and_beyond_ its 'Base' subobject,
    >> is not used at all in that operation. I suppose the following is
    >> even scarier to you:
    >>
    >> class Derived3 : public Derived2 {};
    >> class Derived4 : public Derived3 { std::string name; };
    >>
    >> ...
    >> Derived4 d4;
    >> BaseAssign(d1, d4);
    >>
    >> It shouldn't be. Relax. Take a deep breath. Nothing *bad* is
    >> happening here. If your problem domain prohibits that (for whatever
    >> reason, you did not say anything about the problem domain), you
    >> _could_ disable it by making 'Base' protected base class, but then
    >> LSP cannot be applied... Maybe that's what you want... Speak up,
    >> then.
    >>
    >> V

    >
    > You seem to get angry when people ask civil questions about things
    > they don't understand. I feel like I'm going to be shouted at just
    > for being inquisitive and trying to understand something properly.


    Oh, please...

    <shrug> Angry? I say "relax", and you think I am angry all of
    a sudden? What is it, inferiority complex? Did somebody use to
    shout at you for not understanding something? Never mind. I cannot
    help you fight your demons. I can only help you with C++. So,
    lighten up and ask your questions.

    > So assignment doesn't work "polymorphically"?


    It might, if you declare the assignment operator 'virtual'. But your
    case doesn't have that. It definitely shouldn't do it by default, and
    it doesn't.

    > This differs to C#


    Well... If you learn C++ hoping that it behaves similarly to some
    other language that may have similar letters in its name, I strongly
    urge you not to. C++ is complex enough, but much more logical if you
    shed some preconceptions that have crept in when you were learning
    <insert_other_language_here>.

    > (I
    > wrote a similar test, and found that the object of type Derived1
    > /becomes/ a Derived2 when assigned through a Base reference). I guess
    > this is why boxing works in C# with the universal base class "object".


    I don't know what "boxing" is. I never heard that term used WRT C++
    objects.

    C++ is a _statically_ typed language. Objects in C++ cannot change
    their nature because of a simple assignment of their bases. Type of
    every C++ object is defined at its creation and remains the property
    of the object until the object's destruction. I, for one, find it much
    more logical.

    Base classes are essentially members of their respective derived classes.
    Assigning new values to members doesn't change the nature of objects,
    does it? Only the state. Why should assigning new values to base class
    subjects be any different?

    V
    Victor Bazarov, Jan 21, 2006
    #6
  7. Guest

    Victor Bazarov wrote:
    > wrote:
    > > Victor Bazarov wrote:
    > >> wrote:
    > >>> Victor Bazarov wrote:
    > >>>
    > >>>> wrote:
    > >>>>
    > >>>>> What measures should be taken to avoid this sort of thing?
    > >>>>
    > >>>> WHY?
    > >>>>
    > >>>>
    > >>>>> class Base
    > >>>>> {
    > >>>>> };
    > >>>>>
    > >>>>> class Derived1 : public Base
    > >>>>> {
    > >>>>> private:
    > >>>>> int i, j, k;
    > >>>>> };
    > >>>
    > >>>>> class Derived2 : public Base
    > >>>>> {
    > >>>>> private:
    > >>>>> double l, m, n;
    > >>>>> };
    > >>>>>
    > >>>>> void BaseAssign(Base& lhs, Base& rhs)
    > >>>>> {
    > >>>>> lhs = rhs;
    > >>>>> }
    > >>>>>
    > >>>>> int main(int argc, char *argv[])
    > >>>>> {
    > >>>>> Derived1 d1;
    > >>>>> Derived2 d2;
    > >>>>>
    > >>>>> BaseAssign(d1, d2);
    > >>>>> }
    > >>>>>
    > >>>>> // End code snippet
    > >>>>>
    > >>>>> The only way I can see around preventing this sort of thing is
    > >>>>
    > >>>> Why prevent this sort of thing? Do you experience any problem?
    > >>>> Please elaborate.
    > >>>>
    > >>>>
    > >>>>> declaring the assignment operators and copy constructors of
    > >>>>> non-leaf classes protected, and providing clone/create methods
    > >>>>> instead of letting the operators/constructors be used.
    > >>>>>
    > >>>>> Do people generally just not worry about this sort of thing? I'm
    > >>>>> absolutely paranoid about stuff like this.
    > >>>>
    > >>>> Well, could you share? Maybe I need to become paranoid about it as
    > >>>> well...
    > >>>>
    > >>>> V
    > >>>
    > >>>
    > >>> The classes must be getting sliced... I mean, how can a Derived2 be
    > >>> assigned to a Derived1?
    > >>>
    > >>
    > >> Where did you get that idea, about slicing? The Base subobject of
    > >> 'd1' is simply made _the_same_ as the Base subobject of 'd2'. At
    > >> least that's what it means [to me] *semantically*. Nothing more and
    > >> nothing less.
    > >>
    > >> Whatever 'd1' has _above_and_beyond_ its 'Base' subobject, is kept
    > >> intact. Whatever 'd2' has _above_and_beyond_ its 'Base' subobject,
    > >> is not used at all in that operation. I suppose the following is
    > >> even scarier to you:
    > >>
    > >> class Derived3 : public Derived2 {};
    > >> class Derived4 : public Derived3 { std::string name; };
    > >>
    > >> ...
    > >> Derived4 d4;
    > >> BaseAssign(d1, d4);
    > >>
    > >> It shouldn't be. Relax. Take a deep breath. Nothing *bad* is
    > >> happening here. If your problem domain prohibits that (for whatever
    > >> reason, you did not say anything about the problem domain), you
    > >> _could_ disable it by making 'Base' protected base class, but then
    > >> LSP cannot be applied... Maybe that's what you want... Speak up,
    > >> then.
    > >>
    > >> V

    > >
    > > You seem to get angry when people ask civil questions about things
    > > they don't understand. I feel like I'm going to be shouted at just
    > > for being inquisitive and trying to understand something properly.

    >
    > Oh, please...
    >
    > <shrug> Angry? I say "relax", and you think I am angry all of
    > a sudden? What is it, inferiority complex? Did somebody use to
    > shout at you for not understanding something? Never mind. I cannot
    > help you fight your demons. I can only help you with C++. So,
    > lighten up and ask your questions.


    It's the way you type. You're very abrasive. I understand that you
    don't mean to sound rude, but that's definitely the way you came across
    here. And I don't have time to argue with you about further
    inappropriate comments that you just made.

    > > So assignment doesn't work "polymorphically"?

    >
    > It might, if you declare the assignment operator 'virtual'. But your
    > case doesn't have that. It definitely shouldn't do it by default, and
    > it doesn't.


    How would this work? If we were in BaseAssign and the /virtual/
    assignment operator was called, how would it possibly assign from an
    incompatible type? I'm sorry if I'm getting mixed up, I just need to
    clear this up for my own sanity.

    >
    > > This differs to C#

    >
    > Well... If you learn C++ hoping that it behaves similarly to some
    > other language that may have similar letters in its name, I strongly
    > urge you not to. C++ is complex enough, but much more logical if you
    > shed some preconceptions that have crept in when you were learning
    > <insert_other_language_here>.


    Fortunately I am not doing this, you're jumping to conclusions a bit. I
    barely know C#, but I just about managed to write the equivalent to my
    C++ test program to see what the results were. I only mentioned this as
    an interesting comparison and an example of another way of thinking
    about the problem.

    >
    > > (I
    > > wrote a similar test, and found that the object of type Derived1
    > > /becomes/ a Derived2 when assigned through a Base reference). I guess
    > > this is why boxing works in C# with the universal base class "object".

    >
    > I don't know what "boxing" is. I never heard that term used WRT C++
    > objects.
    >
    > C++ is a _statically_ typed language. Objects in C++ cannot change
    > their nature because of a simple assignment of their bases. Type of
    > every C++ object is defined at its creation and remains the property
    > of the object until the object's destruction. I, for one, find it much
    > more logical.


    Yes. Interestingly enough, it is often said that C# is statically
    typed, despite working in a different way in my test.

    > to
    > Base classes are essentially members of their respective derived classes.
    > Assigning new values to members doesn't change the nature of objects,
    > does it? Only the state. Why should assigning new values base class
    > subjects be any different?


    This is why I wrote my test and asked these questions. Thank you.
    , Jan 21, 2006
    #7
  8. wrote:
    > Victor Bazarov wrote:
    >> wrote:
    >>> So assignment doesn't work "polymorphically"?

    >>
    >> It might, if you declare the assignment operator 'virtual'. But your
    >> case doesn't have that. It definitely shouldn't do it by default,
    >> and it doesn't.

    >
    > How would this work? If we were in BaseAssign and the /virtual/
    > assignment operator was called, how would it possibly assign from an
    > incompatible type? I'm sorry if I'm getting mixed up, I just need to
    > clear this up for my own sanity.


    struct Base {
    virtual Base& operator=(Base& b) { return *this; }
    };

    struct Derived : Base {
    virtual Derived& operator=(Base& b) {
    // do something special
    return *this;
    }
    };

    void foo(Base & b1, Base & b2) {
    b1 = b2;
    }

    int main() {
    Derived d1, d2;
    foo(d1, d2); // conversion to base causes polymorphic assignment
    // to be called
    d1 = d2; // "normal", non-polymorphic assignment is invoked
    }


    Now, study that example. There is a difference between what happens
    when we say 'b1 = b2', and 'd1 = d2'. In the first case, operator=
    from 'Base' is used, and it's virtual, and the true types of objects
    behind 'b1' and 'b2' is 'Derived', so the final overrider is called.

    What it might for, I am not sure. I never had to use it in my own
    work. Search the web for "virtual assignment operator" and you will
    find probably enough information to understand what people use it for.

    Notice, however, that the signature of the Derived::eek:perator=(Base&)
    is only slightly different from Base::eek:perator(Base&). It is required
    to have the same argument, but the return value type can be 'Derived&'.

    I've thought only of one possible use. Imagine the implementation of
    the virtual operator= is like this:

    virtual Derived& operator= (Base &b) {
    try { // now...
    Derived &d = dynamic_cast<Derived&>(b);
    this->Derived::eek:perator=(d); // use the default assignment
    }
    catch(...) {
    this->Base::eek:perator=(b); // fall-back functionality
    }
    }

    Now, if there will be another derived class

    struct Derived2 : Base {};

    and somebody does

    Derived2 d22;
    foo(d1, d2);

    the Derived's operator= will see that the "other" object is not of
    the same type and will fall back onto copying only the Base part.

    Convoluted, I know. Sometimes necessary, maybe. Again, I never had
    to do that in my practice.

    >>
    >>> This differs to C#

    >>
    >> Well... If you learn C++ hoping that it behaves similarly to some
    >> other language that may have similar letters in its name, I strongly
    >> urge you not to. C++ is complex enough, but much more logical if you
    >> shed some preconceptions that have crept in when you were learning
    >> <insert_other_language_here>.

    >
    > Fortunately I am not doing this, you're jumping to conclusions a bit.
    > I barely know C#, but I just about managed to write the equivalent to
    > my C++ test program to see what the results were. I only mentioned
    > this as an interesting comparison and an example of another way of
    > thinking about the problem.


    Whatever. As I said, "if"...

    V
    Victor Bazarov, Jan 21, 2006
    #8
  9. Guest

    Victor Bazarov wrote:
    > wrote:
    > > Victor Bazarov wrote:
    > >> wrote:
    > >>> So assignment doesn't work "polymorphically"?
    > >>
    > >> It might, if you declare the assignment operator 'virtual'. But your
    > >> case doesn't have that. It definitely shouldn't do it by default,
    > >> and it doesn't.

    > >
    > > How would this work? If we were in BaseAssign and the /virtual/
    > > assignment operator was called, how would it possibly assign from an
    > > incompatible type? I'm sorry if I'm getting mixed up, I just need to
    > > clear this up for my own sanity.

    >
    > struct Base {
    > virtual Base& operator=(Base& b) { return *this; }
    > };
    >
    > struct Derived : Base {
    > virtual Derived& operator=(Base& b) {
    > // do something special
    > return *this;
    > }
    > };
    >
    > void foo(Base & b1, Base & b2) {
    > b1 = b2;
    > }
    >
    > int main() {
    > Derived d1, d2;
    > foo(d1, d2); // conversion to base causes polymorphic assignment
    > // to be called
    > d1 = d2; // "normal", non-polymorphic assignment is invoked
    > }
    >
    >
    > Now, study that example. There is a difference between what happens
    > when we say 'b1 = b2', and 'd1 = d2'. In the first case, operator=
    > from 'Base' is used, and it's virtual, and the true types of objects
    > behind 'b1' and 'b2' is 'Derived', so the final overrider is called.
    >
    > What it might for, I am not sure. I never had to use it in my own
    > work. Search the web for "virtual assignment operator" and you will
    > find probably enough information to understand what people use it for.
    >
    > Notice, however, that the signature of the Derived::eek:perator=(Base&)
    > is only slightly different from Base::eek:perator(Base&). It is required
    > to have the same argument, but the return value type can be 'Derived&'.
    >
    > I've thought only of one possible use. Imagine the implementation of
    > the virtual operator= is like this:
    >
    > virtual Derived& operator= (Base &b) {
    > try { // now...
    > Derived &d = dynamic_cast<Derived&>(b);
    > this->Derived::eek:perator=(d); // use the default assignment
    > }
    > catch(...) {
    > this->Base::eek:perator=(b); // fall-back functionality
    > }
    > }
    >
    > Now, if there will be another derived class
    >
    > struct Derived2 : Base {};
    >
    > and somebody does
    >
    > Derived2 d22;
    > foo(d1, d2);
    >
    > the Derived's operator= will see that the "other" object is not of
    > the same type and will fall back onto copying only the Base part.
    >
    > Convoluted, I know. Sometimes necessary, maybe. Again, I never had
    > to do that in my practice.


    Thanks. I had figured out virtual assignment operators in general, I
    just didn't know how it would work with different types of the same
    base through the base pointer. I see now that in cases like this it
    only assigns the base class portion. Thanks.
    , Jan 21, 2006
    #9
  10. Guest

    Victor Bazarov wrote:
    > wrote:
    > > Victor Bazarov wrote:
    > >
    > >> wrote:
    > >>
    > >>>What measures should be taken to avoid this sort of thing?
    > >>
    > >>WHY?
    > >>
    > >>
    > >>>class Base
    > >>>{
    > >>>};
    > >>>
    > >>>class Derived1 : public Base
    > >>>{
    > >>> private:
    > >>> int i, j, k;
    > >>>};
    > >>>
    > >>>class Derived2 : public Base
    > >>>{
    > >>> private:
    > >>> double l, m, n;
    > >>>};
    > >>>
    > >>>void BaseAssign(Base& lhs, Base& rhs)
    > >>>{
    > >>> lhs = rhs;
    > >>>}
    > >>>
    > >>>int main(int argc, char *argv[])
    > >>>{
    > >>> Derived1 d1;
    > >>> Derived2 d2;
    > >>>
    > >>> BaseAssign(d1, d2);
    > >>>}
    > >>>
    > >>>// End code snippet
    > >>>
    > >>>The only way I can see around preventing this sort of thing is
    > >>
    > >>Why prevent this sort of thing? Do you experience any problem? Please
    > >>elaborate.
    > >>
    > >>
    > >>>declaring the assignment operators and copy constructors of non-leaf
    > >>>classes protected, and providing clone/create methods instead of
    > >>>letting the operators/constructors be used.
    > >>>
    > >>>Do people generally just not worry about this sort of thing? I'm
    > >>>absolutely paranoid about stuff like this.
    > >>
    > >>Well, could you share? Maybe I need to become paranoid about it as
    > >>well...
    > >>
    > >>V

    > >
    > >
    > > The classes must be getting sliced... I mean, how can a Derived2 be
    > > assigned to a Derived1?
    > >

    >
    > Where did you get that idea, about slicing? The Base subobject of 'd1' is
    > simply made _the_same_ as the Base subobject of 'd2'. At least that's
    > what it means [to me] *semantically*. Nothing more and nothing less.


    Where do you get the idea that it isn't sliced?

    I have managed to find this page, which explains in a way clearer than
    I could:

    http://icu.sourceforge.net/docs/papers/cpp_report/the_assignment_operator_revisited.html

    Scroll down to "virtual assignment". The author approaches the problem
    first virtual assignment operators and dynamic casts (so that
    incompatible assignments throw, rather than slice), and then later
    improves it with an assert .

    I wouldn't like to do this for every non-trivial class - would you? I
    tend to design around this sort of thing where possible.
    , Jan 22, 2006
    #10
  11. wrote:
    > Victor Bazarov wrote:
    >> wrote:
    >>> Victor Bazarov wrote:
    >>>
    >>>> wrote:
    >>>>
    >>>>> What measures should be taken to avoid this sort of thing?
    >>>>
    >>>> WHY?
    >>>>
    >>>>
    >>>>> class Base
    >>>>> {
    >>>>> };
    >>>>>
    >>>>> class Derived1 : public Base
    >>>>> {
    >>>>> private:
    >>>>> int i, j, k;
    >>>>> };
    >>>>>
    >>>>> class Derived2 : public Base
    >>>>> {
    >>>>> private:
    >>>>> double l, m, n;
    >>>>> };
    >>>>>
    >>>>> void BaseAssign(Base& lhs, Base& rhs)
    >>>>> {
    >>>>> lhs = rhs;
    >>>>> }
    >>>>>
    >>>>> int main(int argc, char *argv[])
    >>>>> {
    >>>>> Derived1 d1;
    >>>>> Derived2 d2;
    >>>>>
    >>>>> BaseAssign(d1, d2);
    >>>>> }
    >>>>>
    >>>>> // End code snippet
    >>>>>
    >>>>> The only way I can see around preventing this sort of thing is
    >>>>
    >>>> Why prevent this sort of thing? Do you experience any problem?
    >>>> Please elaborate.
    >>>>
    >>>>
    >>>>> declaring the assignment operators and copy constructors of
    >>>>> non-leaf classes protected, and providing clone/create methods
    >>>>> instead of letting the operators/constructors be used.
    >>>>>
    >>>>> Do people generally just not worry about this sort of thing? I'm
    >>>>> absolutely paranoid about stuff like this.
    >>>>
    >>>> Well, could you share? Maybe I need to become paranoid about it as
    >>>> well...
    >>>>
    >>>> V
    >>>
    >>>
    >>> The classes must be getting sliced... I mean, how can a Derived2 be
    >>> assigned to a Derived1?
    >>>

    >>
    >> Where did you get that idea, about slicing? The Base subobject of
    >> 'd1' is simply made _the_same_ as the Base subobject of 'd2'. At
    >> least that's what it means [to me] *semantically*. Nothing more and
    >> nothing less.

    >
    > Where do you get the idea that it isn't sliced?


    Slicing happens if you _construct_ a base class object from a derived
    class object. In your case no construction happens. Assignment in its
    pure form is _giving_new_values_ to pre-existing objects. There is no
    room for slicing there. Period. I guess some have been misusing the
    term "slicing".

    > I have managed to find this page, which explains in a way clearer than
    > I could:
    >
    > http://icu.sourceforge.net/docs/papers/cpp_report/the_assignment_operator_revisited.html
    >
    > Scroll down to "virtual assignment". The author approaches the problem
    > first virtual assignment operators and dynamic casts (so that
    > incompatible assignments throw, rather than slice), and then later
    > improves it with an assert .
    >
    > I wouldn't like to do this for every non-trivial class - would you? I
    > tend to design around this sort of thing where possible.


    How is what the author is writing relevant to your case? I am just
    wondering.

    Besides, even considering that his fragment

    X* x;
    void setX(const X& newX) {
    x = newX;
    }

    _is_ part of a class (who in their right mind is going to keep global
    pointers and set them using some function?), then it still has nothing
    to do with slicing because it's not the _object_ that's assigned, it's
    the pointer.

    As I already said in another reply, I've not encountered the need to
    define a virtual assignment operator in my entire C++ career. That is
    not necessarily an argument against them, it's just an indication that
    the need in them is rare.

    V
    Victor Bazarov, Jan 22, 2006
    #11
  12. Guest

    Victor Bazarov wrote:
    > wrote:
    > > Victor Bazarov wrote:
    > >> wrote:
    > >>> Victor Bazarov wrote:
    > >>>
    > >>>> wrote:
    > >>>>
    > >>>>> What measures should be taken to avoid this sort of thing?
    > >>>>
    > >>>> WHY?
    > >>>>
    > >>>>
    > >>>>> class Base
    > >>>>> {
    > >>>>> };
    > >>>>>
    > >>>>> class Derived1 : public Base
    > >>>>> {
    > >>>>> private:
    > >>>>> int i, j, k;
    > >>>>> };
    > >>>>>
    > >>>>> class Derived2 : public Base
    > >>>>> {
    > >>>>> private:
    > >>>>> double l, m, n;
    > >>>>> };
    > >>>>>
    > >>>>> void BaseAssign(Base& lhs, Base& rhs)
    > >>>>> {
    > >>>>> lhs = rhs;
    > >>>>> }
    > >>>>>
    > >>>>> int main(int argc, char *argv[])
    > >>>>> {
    > >>>>> Derived1 d1;
    > >>>>> Derived2 d2;
    > >>>>>
    > >>>>> BaseAssign(d1, d2);
    > >>>>> }
    > >>>>>
    > >>>>> // End code snippet
    > >>>>>
    > >>>>> The only way I can see around preventing this sort of thing is
    > >>>>
    > >>>> Why prevent this sort of thing? Do you experience any problem?
    > >>>> Please elaborate.
    > >>>>
    > >>>>
    > >>>>> declaring the assignment operators and copy constructors of
    > >>>>> non-leaf classes protected, and providing clone/create methods
    > >>>>> instead of letting the operators/constructors be used.
    > >>>>>
    > >>>>> Do people generally just not worry about this sort of thing? I'm
    > >>>>> absolutely paranoid about stuff like this.
    > >>>>
    > >>>> Well, could you share? Maybe I need to become paranoid about it as
    > >>>> well...
    > >>>>
    > >>>> V
    > >>>
    > >>>
    > >>> The classes must be getting sliced... I mean, how can a Derived2 be
    > >>> assigned to a Derived1?
    > >>>
    > >>
    > >> Where did you get that idea, about slicing? The Base subobject of
    > >> 'd1' is simply made _the_same_ as the Base subobject of 'd2'. At
    > >> least that's what it means [to me] *semantically*. Nothing more and
    > >> nothing less.

    > >
    > > Where do you get the idea that it isn't sliced?

    >
    > Slicing happens if you _construct_ a base class object from a derived
    > class object. In your case no construction happens. Assignment in its
    > pure form is _giving_new_values_ to pre-existing objects. There is no
    > room for slicing there. Period. I guess some have been misusing the
    > term "slicing".


    It might not be a perfect term for what's going on (you could say
    "sliced assignment" instead), but that doesn't make the problem
    (however contrived it might be) any less real. I certainly feel better
    for investigating it.

    >
    > > I have managed to find this page, which explains in a way clearer than
    > > I could:
    > >
    > > http://icu.sourceforge.net/docs/papers/cpp_report/the_assignment_operator_revisited.html
    > >
    > > Scroll down to "virtual assignment". The author approaches the problem
    > > first virtual assignment operators and dynamic casts (so that
    > > incompatible assignments throw, rather than slice), and then later
    > > improves it with an assert .
    > >
    > > I wouldn't like to do this for every non-trivial class - would you? I
    > > tend to design around this sort of thing where possible.

    >
    > How is what the author is writing relevant to your case? I am just
    > wondering.


    Looking at it now, his code sample seems wrong. I think this:

    X* x;

    void setX(const X& newX) {
    x = &newX;
    }

    should actually be this:

    X x;

    void setX(const X& newX) {
    x = newX;
    }

    to match his text (perhaps he DOESN'T explain it better than I could
    after all!). But you see how THAT relates to my problem and the
    solutions written after it, don't you?

    >
    > Besides, even considering that his fragment
    >
    > X* x;
    > void setX(const X& newX) {
    > x = newX;
    > }
    >
    > _is_ part of a class (who in their right mind is going to keep global
    > pointers and set them using some function?), then it still has nothing
    > to do with slicing because it's not the _object_ that's assigned, it's
    > the pointer.
    >
    > As I already said in another reply, I've not encountered the need to
    > define a virtual assignment operator in my entire C++ career. That is
    > not necessarily an argument against them, it's just an indication that
    > the need in them is rare.


    The problem is very contrived and artificial. I just wanted to
    investigate as part of the process of understanding the language as
    much as possible.

    Interestingly, the guy in that article seems to have received a lot of
    e-mails from people (including his own boss) who think otherwise, which
    prompted him to write about the problem. Obviously some people /do/
    have issues with polymorphic assignment in this way. I can see both
    sides, really - objects might end up in an invalid state of only the
    Base part of them gets assigned, whilst on the other hand - when is
    this ever going to happen, and is it simply a side effect of
    over-complex design?

    Thanks for your input, Victor.
    , Jan 22, 2006
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Karl Heinz Buchegger
    Replies:
    3
    Views:
    523
    Karl Heinz Buchegger
    Aug 6, 2003
  2. John Harrison
    Replies:
    0
    Views:
    484
    John Harrison
    Aug 6, 2003
  3. Stephan Br?nnimann

    "Assignment" through base class

    Stephan Br?nnimann, Aug 9, 2004, in forum: C++
    Replies:
    2
    Views:
    369
    Stephan Br?nnimann
    Aug 9, 2004
  4. user
    Replies:
    1
    Views:
    395
    Jack Klein
    Jul 11, 2005
  5. siddhu
    Replies:
    12
    Views:
    625
    Gianni Mariani
    Jun 6, 2007
Loading...

Share This Page