slicing copy, what's wrong with abstract class?

B

baibaichen

i have written some code to verify how to disable slicing copy
according C++ Gotchas item 30

the follow is my class hierarchy, and note that B is abstract class!!

class B
{
public:
explicit B(INT32 i =0):i_(i){}
virtual ~B(){}
virtual void do_it() = 0;
virtual void pay() const = 0;
protected:
private:
INT32 i_;
};

class D: public B
{
public:
explicit D(INT32 j=0):B(),j_(j){}
virtual void do_it()
{
j_ = 100;
}
virtual void pay() const {}
protected:
private:
INT32 j_;
};

accring to book, the following code can not be compiled, however it can
be compiled in the vc7.1

D d,d1(1);
B *const pb=&d1;
*pb = d; // B is abstract class, and this should be issued a compile
error according to the book
// however it can be compiled in the vc7.1

what's something with me or the book?

thanks
 
E

Earl Purple

baibaichen said:
i have written some code to verify how to disable slicing copy
according C++ Gotchas item 30

the follow is my class hierarchy, and note that B is abstract class!!

class B
{
public:
explicit B(INT32 i =0):i_(i){}
virtual ~B(){}
virtual void do_it() = 0;
virtual void pay() const = 0;
protected:
private:
INT32 i_;
};

class D: public B
{
public:
explicit D(INT32 j=0):B(),j_(j){}
virtual void do_it()
{
j_ = 100;
}
virtual void pay() const {}
protected:
private:
INT32 j_;
};

accring to book, the following code can not be compiled, however it can
be compiled in the vc7.1

D d,d1(1);
B *const pb=&d1;
*pb = d; // B is abstract class, and this should be issued a compile
error according to the book
// however it can be compiled in the vc7.1


I don't have the book but are you sure that's what it says? Why
shouldn't it compile, operator= is public in B. Make it protected and
it won't compile but you'll still be able to assign D.
 
A

Axter

baibaichen said:
i have written some code to verify how to disable slicing copy
according C++ Gotchas item 30

the follow is my class hierarchy, and note that B is abstract class!!

class B
{
public:
explicit B(INT32 i =0):i_(i){}
virtual ~B(){}
virtual void do_it() = 0;
virtual void pay() const = 0;
protected:
private:
INT32 i_;
};

class D: public B
{
public:
explicit D(INT32 j=0):B(),j_(j){}
virtual void do_it()
{
j_ = 100;
}
virtual void pay() const {}
protected:
private:
INT32 j_;
};

accring to book, the following code can not be compiled, however it can
be compiled in the vc7.1

D d,d1(1);
B *const pb=&d1;
*pb = d; // B is abstract class, and this should be issued a compile
error according to the book
// however it can be compiled in the vc7.1

what's something with me or the book?

To my knowledge, there is no way to avoid slicing with abstract logic.

With concrete types, you can avoid slicing by making your class
non-copyable.
This can be done by making the copy constructor and assignment operator
private, and with no implementation.
But this method will not work on abstract types.
Moreover, if you're passing raw pointers, you shouldn't get any
slicing.
With abstract pointers, you can get slicing when using smart pointers
that have clone logic.
 
E

Earl Purple

Axter said:
To my knowledge, there is no way to avoid slicing with abstract logic.

Didn't you read my above post? I just showed how it can be done.
With concrete types, you can avoid slicing by making your class
non-copyable.
This can be done by making the copy constructor and assignment operator
private, and with no implementation.
But this method will not work on abstract types.

No but you can make copying and copy-construction protected. That
prevents slicing whilst allowing the derived classes to have copy and
assignment semantics.
Moreover, if you're passing raw pointers, you shouldn't get any
slicing.
With abstract pointers, you can get slicing when using smart pointers
that have clone logic.

No doubt yet another case for the Axter clone-pointer.
 
A

Axter

Earl said:
Didn't you read my above post? I just showed how it can be done.

That can only prevent slicing using value semantics. It can not
prevent slicing using pointer semantics.
No but you can make copying and copy-construction protected. That
prevents slicing whilst allowing the derived classes to have copy and
assignment semantics.

Again, that does not prevent slicing in pointer semantics. Only for
value semantics.
No doubt yet another case for the Axter clone-pointer.

Sorry to say, but even my clone smart pointer does not prevent slicing.
Although it has assert logic for slicing, that will only catch the
slicing at runtime, and will not prevent it from compiling.

I have not found any method that can stop slicing for abstract types at
compile time.
 
?

=?iso-8859-1?q?Stephan_Br=F6nnimann?=

Axter wrote:
[snip]
I have not found any method that can stop slicing for abstract types at
compile time.

For my understanding: could you please show how in the
following code Base can be sliced?
class Base {
public:
Base(int i = 0);
virtual ~Base();
protected:
Base(const Base& rhs);
Base& operator=(const Base& rhs);

private:
int i_;
};

Regards, Stephan
 
A

Axter

Stephan said:
Axter wrote:
[snip]
I have not found any method that can stop slicing for abstract types at
compile time.

For my understanding: could you please show how in the
following code Base can be sliced?
class Base {
public:
Base(int i = 0);
virtual ~Base();
protected:
Base(const Base& rhs);
Base& operator=(const Base& rhs);

private:
int i_;
};
FYI: Your example ahs the private and protected in reverse location.

In order to copy an abstract type, you have to use a clone method
similar to that used by boost pointer containers, or a clone method
similar to the one used by the following clone smart pointer:
http://code.axter.com/copy_ptr.h

Both method can get sliced even when base class has private copy
constructor and assignment operator.

The following example code is pulled from a recent thread in the boost
newsgroup.

class BasesS1
{
public:
virtual BasesS1* do_clone()const=0;
virtual string WhoAmI()const=0;
BasesS1(int i = 0):m_i(i){}
private:
BasesS1( const BasesS1& );
BasesS1& operator=( const BasesS1& );
protected:
int m_i;
};

class DerivedS1 : public BasesS1
{
public:
DerivedS1(int i = 0):BasesS1(i){}
BasesS1* do_clone()const{return new DerivedS1();}
string WhoAmI()const{return "DerivedS1";}
};

class DerivedDerivedS1 : public DerivedS1
{
public:
DerivedDerivedS1(int i = 0):DerivedS1(i){}
string WhoAmI()const{return "DerivedDerivedS1";}
};

void somefunction()
{
boost::ptr_vector<BasesS1> vBaseS1_cpy1;
vBaseS1_cpy1.push_back(new DerivedDerivedS1(123));
boost::ptr_vector<BasesS1> vBaseS1_cpy2(vBaseS1_cpy1.begin(),
vBaseS1_cpy1.end());
cout << vBaseS1_cpy2[0].WhoAmI() << endl;
}

The above code will produce slicing, even though the base class has
private copy constructor and assignment operator.
 
E

Earl Purple

Axter said:
FYI: Your example ahs the private and protected in reverse location.

No it doesn't.
The following example code is pulled from a recent thread in the boost
newsgroup.

class BasesS1
{
public:
virtual BasesS1* do_clone()const=0;
virtual string WhoAmI()const=0;
BasesS1(int i = 0):m_i(i){}
private:
BasesS1( const BasesS1& );
BasesS1& operator=( const BasesS1& );
protected:
int m_i;
};

class DerivedS1 : public BasesS1
{
public:
DerivedS1(int i = 0):BasesS1(i){}
BasesS1* do_clone()const{return new DerivedS1();}
string WhoAmI()const{return "DerivedS1";}
};

class DerivedDerivedS1 : public DerivedS1
{
public:
DerivedDerivedS1(int i = 0):DerivedS1(i){}
string WhoAmI()const{return "DerivedDerivedS1";}
};

1. DerivedS1 is not abstract. The problem was to prevent an ABSTRACT
base-class from being sliced, which can be done by giving it a
protected copy-constructor and protected assignment.

2. Your derived class is implementing the copying of the base class
because the base-class has left in a loophole, i.e. making its members
protected.

3. If you don't write the derived class properly it won't work, i.e. if
you overload the copy-constructor to not actually copy any of its
members. Why would you do that though?

4. You didn't answer the above poster's question.
 
A

Axter

Earl said:
No it doesn't.


1. DerivedS1 is not abstract. The problem was to prevent an ABSTRACT
base-class from being sliced, which can be done by giving it a
protected copy-constructor and protected assignment.

2. Your derived class is implementing the copying of the base class
because the base-class has left in a loophole, i.e. making its members
protected.

3. If you don't write the derived class properly it won't work, i.e. if
you overload the copy-constructor to not actually copy any of its
members. Why would you do that though?

4. You didn't answer the above poster's question.

I did answer the question. Abstract types can not be duplicated
directly, and must use an alternate interface like that used by boost
pointer containers or the copy_ptr.
Regardless of which method you use to copy an abstract type, it can get
slice, even with non-copyable base class.

In this thread, the purpose of making the base class non-copyable is to
stop slicing at compile time, and more specifically, to stop erroneous
slicing code from compiling. Making the base class non-copyable does
not stop erroneous abstract slicing from compiling.

Since this question is specifically referring to an abstract class,
this is more relevant.
 
A

Axter

Earl said:
No it doesn't.


1. DerivedS1 is not abstract. The problem was to prevent an ABSTRACT
base-class from being sliced,

Base class don't get slice. It's derive classes that get slice. The
example shows a derived class getting slice.
 
?

=?iso-8859-1?q?Stephan_Br=F6nnimann?=

Axter said:
Stephan said:
Axter wrote:
[snip]
I have not found any method that can stop slicing for abstract types at
compile time.

For my understanding: could you please show how in the
following code Base can be sliced?
class Base {
public:
Base(int i = 0);
virtual ~Base();
protected:
Base(const Base& rhs);
Base& operator=(const Base& rhs);

private:
int i_;
};
FYI: Your example ahs the private and protected in reverse location.

In order to copy an abstract type, you have to use a clone method
similar to that used by boost pointer containers, or a clone method
similar to the one used by the following clone smart pointer:
http://code.axter.com/copy_ptr.h

Both method can get sliced even when base class has private copy
constructor and assignment operator.

The following example code is pulled from a recent thread in the boost
newsgroup.

class BasesS1
{
public:
virtual BasesS1* do_clone()const=0;
virtual string WhoAmI()const=0;
BasesS1(int i = 0):m_i(i){}
private:
BasesS1( const BasesS1& );
BasesS1& operator=( const BasesS1& );
protected:
int m_i;
};

class DerivedS1 : public BasesS1
{
public:
DerivedS1(int i = 0):BasesS1(i){}
BasesS1* do_clone()const{return new DerivedS1();}
string WhoAmI()const{return "DerivedS1";}
};

class DerivedDerivedS1 : public DerivedS1
{
public:
DerivedDerivedS1(int i = 0):DerivedS1(i){}
string WhoAmI()const{return "DerivedDerivedS1";}
};

void somefunction()
{
boost::ptr_vector<BasesS1> vBaseS1_cpy1;
vBaseS1_cpy1.push_back(new DerivedDerivedS1(123));
boost::ptr_vector<BasesS1> vBaseS1_cpy2(vBaseS1_cpy1.begin(),
vBaseS1_cpy1.end());
cout << vBaseS1_cpy2[0].WhoAmI() << endl;
}

The above code will produce slicing, even though the base class has
private copy constructor and assignment operator.

Your statement was: "I have not found any method that can stop slicing
for abstract types at compile time."
Your example that slicing can be possible does not prove your original
statement.
Your code violates Scott Meyer MEC++ "Item 33: Make non-leaf classes
abstract." Add
class DerivedS1 : public BasesS1 {
public:
~DerivedS1() = 0;
};
and see what the compiler tells you in
BasesS1* DerivedS1::do_clone() const;

BTW: I think the semantics of clone(), do_clone() usually is to return
a copy
and not a default constructed instance of the class.

Regards, Stephan
 
E

Earl Purple

Axter said:
Base class don't get slice. It's derive classes that get slice. The
example shows a derived class getting slice.

But you didn't slice using the original base class, you derived a class
from it, then derived another class from that and sliced the second
derivation using the first one.

Now given these classes:

class Base
{
float itsFloat;
public:
explicit Base( float f=0 ) : itsFloat( f )
{
}

protected:
Base( const Base & b ) : itsFloat( b.itsFloat )
{
}

Base& operator=( const Base& b )
{
itsFloat = b.itsFloat;
return *this;
}

virtual std::eek:stream & output( std::eek:stream & os ) const
{
return os << itsFloat;
}
};

class Derived : public Base
{
double itsDouble;
public:
explicit Derived( double d=0.0 ) : Base( static_cast<float>(d*d) ),
itsDouble( d )
{
}

virtual std::eek:stream & output( std::eek:stream & os ) const
{
Base::eek:utput( os );
return os << ' ' << itsDouble;
}
};

std::eek:stream & operator<<( std::eek:stream & os, const Base & b )
{
return b.output( os );
}

Now show me how you can slice Derived. You must have a Derived, then
copy it or assign it to another derived but only the float part should
be transferred. So the float must not be the square of the double (or
overflow if appropriate).
 
?

=?iso-8859-1?q?Stephan_Br=F6nnimann?=

Actually my original posted code was bogous (MEC++, item 33!) because
Base is not an abstract base class. We should make its destructor pure
virtual

class Base {
public:
virtual ~Base() = 0;
};

Base::~Base() {}

Regards, Stephan
 
E

Earl Purple

Stephan said:
Actually my original posted code was bogous (MEC++, item 33!) because
Base is not an abstract base class. We should make its destructor pure
virtual

class Base {
public:
virtual ~Base() = 0;
};

Base::~Base() {}

Regards, Stephan

Yes, in my post above I forgot to give Base a virtual destructor
altogether.
 
A

Axter

Stephan said:
Axter said:
Stephan said:
Axter wrote:
[snip]
I have not found any method that can stop slicing for abstract types at
compile time.

For my understanding: could you please show how in the
following code Base can be sliced?
class Base {
public:
Base(int i = 0);
virtual ~Base();
protected:
Base(const Base& rhs);
Base& operator=(const Base& rhs);

private:
int i_;
};
FYI: Your example ahs the private and protected in reverse location.

In order to copy an abstract type, you have to use a clone method
similar to that used by boost pointer containers, or a clone method
similar to the one used by the following clone smart pointer:
http://code.axter.com/copy_ptr.h

Both method can get sliced even when base class has private copy
constructor and assignment operator.

The following example code is pulled from a recent thread in the boost
newsgroup.

class BasesS1
{
public:
virtual BasesS1* do_clone()const=0;
virtual string WhoAmI()const=0;
BasesS1(int i = 0):m_i(i){}
private:
BasesS1( const BasesS1& );
BasesS1& operator=( const BasesS1& );
protected:
int m_i;
};

class DerivedS1 : public BasesS1
{
public:
DerivedS1(int i = 0):BasesS1(i){}
BasesS1* do_clone()const{return new DerivedS1();}
string WhoAmI()const{return "DerivedS1";}
};

class DerivedDerivedS1 : public DerivedS1
{
public:
DerivedDerivedS1(int i = 0):DerivedS1(i){}
string WhoAmI()const{return "DerivedDerivedS1";}
};

void somefunction()
{
boost::ptr_vector<BasesS1> vBaseS1_cpy1;
vBaseS1_cpy1.push_back(new DerivedDerivedS1(123));
boost::ptr_vector<BasesS1> vBaseS1_cpy2(vBaseS1_cpy1.begin(),
vBaseS1_cpy1.end());
cout << vBaseS1_cpy2[0].WhoAmI() << endl;
}

The above code will produce slicing, even though the base class has
private copy constructor and assignment operator.

Your statement was: "I have not found any method that can stop slicing
for abstract types at compile time."
Your example that slicing can be possible does not prove your original
statement.
Your code violates Scott Meyer MEC++ "Item 33: Make non-leaf classes
abstract." Add

Yes, you're right. However, code that performs slicing on a concrete
type is usually code that is breaking one rule or another.
The purpose to make it non-copyable, is to stop erroneous slicing code
from compiling.
You're absolutely right, in that the example code is breaking good
coding standards.
But the point is not whether the example code is breaking good coding
standards.
The point is whether non-copyable abstract class can prevent slicing
when using code that is breaking good coding standards.
It can not. Non-copyable method should be used for concrete types, and
it has little value for abstract types.

BTW: I think the semantics of clone(), do_clone() usually is to return
a copy
and not a default constructed instance of the class.

Yes, your right.
The problem is if you make the base copy constructor private, then you
have to use a different constructor, other then the copy constructor.
For this example, I wanted to keep it simple, so I just used the
default constructor.
 
A

Axter

Earl said:
But you didn't slice using the original base class, you derived a class
from it, then derived another class from that and sliced the second
derivation using the first one.

The base class is being used via smart pointer.

Regardless of the method, the slicing still occurs, and the
non-copyable method does not prevent slicing on an abstract type.

If you're only concerned about the direct derived type from getting
slice, then I would recommed using the copy_ptr, cow_ptr, or smart_ptr.

http://code.axter.com/cow_ptr.h
http://code.axter.com/copy_ptr.h
http://code.axter.com/smart_ptr.h

They will stop slicing, without having to make the abstract class
non-copyable, but only of the direct derived type.
 
E

Earl Purple

Axter said:
Yes, you're right. However, code that performs slicing on a concrete
type is usually code that is breaking one rule or another.
The purpose to make it non-copyable, is to stop erroneous slicing code
from compiling.
You're absolutely right, in that the example code is breaking good
coding standards.
But the point is not whether the example code is breaking good coding
standards.
The point is whether non-copyable abstract class can prevent slicing
when using code that is breaking good coding standards.
It can not. Non-copyable method should be used for concrete types, and
it has little value for abstract types.

We write our classes for others to use. We are trying to prevent others
from accidentally slicing. By writing to strict standards we are able
to achieve this.
 
A

Axter

Earl said:
We write our classes for others to use. We are trying to prevent others
from accidentally slicing. By writing to strict standards we are able
to achieve this.

I agree. But if you create a derived class, someone else could create
a derived-derived class from your class. Even if you write your class
to a strict standard, this does not prevent others from slicing your
class.
It's not achievable. That is, unless you have a method to stop your
derived class from being derived itself. Like a friend factory method
and private constructors.
But than, (IMHO) that would make the class less reusable and/or less
generic.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top