2

I read below statements in Addison Wesley FAQs.

Beware: passing objects by value can be dangerous in some situations. Often it is better to pass objects by reference-to-const than to pass them by value. For example, pass-by-value won't work if the destination type is an abstract base class and can result in erroneous behavior at runtime if the parameter's class has derived classes. However if the class of the parameter is guaranteed not to have derived classes, and if the function being called needs a local copy to work with, pass-by-value can be useful.

How it can be erroneous behavior at runtime if destination type is an Abstract class and if the parameter's class has derived class ? Does copy constructor solve this problem ? If so, how ? Thank you.

EDIT: So, should the statement above be "erroneous behavior at compile time" ? Not "runtime" .

bjskishore123
  • 6,144
  • 9
  • 44
  • 66
  • 2
    see http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c – Idan K Oct 11 '10 at 13:04
  • This is not exactly a duplicate of the slicing question. Here we have an *abstract* base class. – sellibitze Oct 11 '10 at 13:11
  • Could you provide the link to the FAQ?? – liaK Oct 11 '10 at 13:15
  • @liak: I got that ebook from a friend. C++ FAQs 2nd edition. Not sure where he got and Free or not. May be you can try downloading from http://www.torrenthound.com/hash/48ef32ad9461a8af05bd7f2e023c707de2f78478/torrent-info/Addison-Wesley-C%2B%2B-FAQs,-Second-Edition – bjskishore123 Oct 11 '10 at 13:21

5 Answers5

7

There are two things here, and you seem to be mixing them up:

1) If the destination type is an abstract class and you pass by value, there will be no runtime error: it won't compile, period.

2) When there will be a runtime error is if the destination type is concrete but has derived classes. Consider this example:

#include <iostream>

struct A {
    virtual void f() { std::cout << "A\n"; }
};
struct B : public A {
    virtual void f() { std::cout << "B\n"; }
};

void func(A a){
    a.f();
}

int main() {
    func( B() );
}

It compiles fine, but outputs A. If you change the parameter to A&, polymorphism works as expected and B is printed. This problem is known as slicing, and there's a nice question here on SO about it.

As a general rule of thumb, if the class has any virtual functions you should a) declare the destructor as virtual and b) avoid passing it by value.

I actually avoid passing any parameter by value, unless I'm sure it's a primitive type such as an int. If you're writing a template, you're better off accepting all parameters as const T&.

Community
  • 1
  • 1
Pedro d'Aquino
  • 5,130
  • 6
  • 36
  • 46
6

How it can be erroneous behavior at runtime if destination type is an Abstract class and if the parameter's class has derived class ?

It won't even compile. "Pass by value" means that you would try to copy the abstract class parts of the argument to a new object. But you cannot create an object with an abstract class as its mosted derived class because it's abstract.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
3

It won't even compile..

From Standard docs 10.4.3,

An abstract class shall not be used as a parameter type, as a function return type, or as the type of an explicit conversion. Pointers and references to an abstract class can be declared.

liaK
  • 11,422
  • 11
  • 48
  • 73
1

OK, suppose we have the following hierarchy

#include <iostream>
class Base
{
public:
   Base(): BaseNum(42)
   {}
   virtual void f()
   {
      std::cout << BaseNum;
   }
   virtual ~Base() 
   {
   }
protected:
   int BaseNum;
};

class Derived: public Base
{
public:
   Derived(): DerivedNum(14)
   {}
   virtual void f()
   {
      std::cout << DerivedNum + BaseNum;
   }
private:
   int DerivedNum;
};

If you have a function taking Base by value and you pass Derived instead, the derived object is first converted to base(by truncating any specific fo Derived information, like DerivedNum) and then copied into the parameter. Thus, in the function you will LOSE any information that was specific to derived. This is known as slicing. Inside the function, if you called the virtual f() function, the Base::f would be called no matter Derived was passed. If you passed by reference or pointer, no copying would be done. And converting from Derived& to Base& preserves the dynamic type information because no actual copying and therefore slicing is done. Sure, you couldn't use the Base& as Derived&, but if you called the f, Derived::f would be called. If Base were abstract, then it would be a compiler error, because when you pass by value, the parameter must be instantiated via a copy constructor, and, as we know, an abstract class cannot be instantiated. HTH.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
0

I doubt passing derived class instances by value to be possible at all. After all the size of the passed value must be known at compile time. However the size of the abstract base class and the derived class can (and most likely will) differ. The resulting behavior is pretty much undefined.

Helmut Grohne
  • 6,578
  • 2
  • 31
  • 67
  • 1
    "...to be possible at all." Well, it sure is possible if the base class is not abstract. Slicing will occur. – sellibitze Oct 11 '10 at 13:08