Loading ...
Sorry, an error occurred while loading the content.

Re: [XP] C++ stack objects [OT?] (was: Continous integration without version control)

Expand Messages
  • C. Keith Ray
    ... quick lesson on slicing -- the bane of C++ stack-based objects: class A { public: virtual void foo(); int myAa; }; class B { public: virtual void foo();
    Message 1 of 2 , Sep 1, 2001
      > --- In extremeprogramming@y..., "C. Keith Ray" <ckeithray@h...> wrote:
      >> And then a Duh! Moment when I realized that maybe Mock objects COULD have
      >> been useful... but only just barely, this is C++ and using Mock objects
      >> would have required not using stack-based objects (no polymorphism for
      >> stack-based objects), so I'd need a factory object and a some kind of smart
      >> pointer template class, which can be a lot harder to write than you might
      >> think. [I really regret the popularization of C++.]
      > This might be better offline, but I don't understand what you mean by "no
      > polymorphism for stack-based objects". An object is an object, whether it
      > was created on the stack (local), in data space (global), or on the heap
      > (using new). If the class has polymorphism, any object of that class will
      > as well.

      quick lesson on "slicing" -- the bane of C++ stack-based objects:

      class A
      virtual void foo();
      int myAa;

      class B
      virtual void foo(); //override parent method
      int myBa;

      stack-allocated objects ('by value' instead of 'pointer' or 'reference'):

      A a; a.foo(); // calls A::foo. 'a' uses 8 bytes of stack memory.
      B b; b.foo(); // calls B::foo 'b' uses 12 bytes of stack memory.

      a = b;
      a.foo(); // calls A::foo. 'a' still has only 8 bytes of stack memory.

      Assigning 'b' to 'a' does NOT make 'a' refer to a 'B' object. Instead, 'a'
      still refers to an 'A' object. The assignment not only slices off the member
      variable 'myBa', but it also slices off the virtual function table pointer
      that pointed to the overridden method 'B::foo()'.

      Thus: you can only use concrete classes as stack-based objects (variables
      declared "by value" inside a function). To use a mock object that has the
      same abstract base class as the production class, I would have to use a
      pointer or a smart-pointer, referring to a heap-allocated object.

      The reason I am using a stack-based objects, is because C++ guarantees that
      the object's destructor will be called when its variable goes out of scope
      -- even if an exception is thrown. Since standard C++ does not have
      "try...finally" syntax, the destructors of stack-allocated objects are used
      to make sure clean-up is performed in the presence of exceptions.

      This 'slicing problem' is unique to C++ as far as I know. It comes from
      attempting to make a C++ 'class' equivalent to a C 'struct' (in various
      circumstances...blah blah.)

      If Objective-C had been freely given away like CFront was, maybe we would
      not be having this conversation, since Objective-C creates all objects on
      the heap (no matter where the variable is declared), just like every other
      OO language except C++.

      There is a solution, but it requires pointers...

      class AWrapper
      A* myAValue;
      delete myAValue;

      // 'new' allocates objects in the heap instead of the stack.
      AWrapper a; a.myAValue = new A; a.myAValue->foo(); // calls A::foo
      AWrapper b; b.myAValue = new B; a.myAValue->foo(); // calls B::foo

      a = b; // memory leak! (no garbage collection in std C++)
      a.myAValue->foo(); // calls B::foo

      To prevent the memory leak, one needs to define "operator=" to delete the
      current object's pointer IF the assignment is not from self to self
      (ie. "a = a").

      To avoid accessing the pointer member variable directly for calling virtual
      functions from outside AWrapper, one can define "operator->". which is a bit
      of C++ trickery that I don't want to get into here.

      There is a bug in this example code -- the B object gets deleted twice,
      because it is referred to by 'a' and 'b'. This usually causes a nice crash,
      so you need your wrapper class to keep count of how many AWrapper variables
      are referring to the same object. This complicates the implementation of
      "operator=", the destructor, constructor, and copy-constructor, and so on.

      This is why sometimes the question "can you do XP with C++?" sometimes comes
      up. The language requires about 10 times more effort than Smalltalk, and at
      least twice as much effort as Java, and requires a lot of care to enable
      using Mock objects, for example.

      I recently found the "Boost" C++ library at <http://www.boost.org>, which
      provides a template class for smart pointers. If I had found that site 18
      days ago, I could have used smart pointers, a factory object, and mock
      objects for my test-first programming.
    Your message has been successfully submitted and would be delivered to recipients shortly.