Questions of designing a class

A

alexhong2001

When design a class, should always make it "derivable" as a base class? Is
there really a situation that the designed class not "derivable"?

When should make a member "protected"? Only when allowing the derived
class(es) directly access it?

Should destructor always be virtual?

Thanks for your comments!
 
C

Claudio Puviani

alexhong2001 said:
When design a class, should always make it "derivable" as a base class?

No. Derivation is something you need to plan and it has non-trivial efficiency
costs.
Is there really a situation that the designed class not "derivable"?

It's not a question of being "derivable", but rather of whether it makes sense
to derive from it, and in many cases, it doesn't. For example, no one but a rank
beginner would derive from std::string or std::vector. The derived class
couldn't be used polymorphically and would almost certainly violate the Liskov
Substitutability Principle.
When should make a member "protected"?

I hate generalizations, but in this case, "rarely" is a good answer (and
arguably, "never" might be a better one). Protected members make it difficult to
modify a base class because the derived classes come to depend on its internals.
If you absolutely insist on having protected members, I'd STRONGLY urge you to
make them protected methods and not protected data members.
Should destructor always be virtual?

No, for the same reasons as outlined in the first answer.

Claudio Puviani
 
P

Phlip

alexhong2001 said:
When design a class, should always make it "derivable" as a base class?

You might think you can never ever change a class once written. People who
think that tend to make too many things virtual.

If you write lots of tests at the same time as you code, you become free to
implement only the simplest design that passes the tests. But if you then
add more tests requesting more features, you can then add code that passes
the tests, and refactor that code into a new, clean design.

Following this technique keeps you out of the debugger, and prevents you
from over-designing classes. You will make classes that contain behavior -
not specifically classes designed to be base classes. But when the time
comes to merge interfaces into a base class, after the change you can test
to ensure changing didn't add a bug.

Read /Design Patterns/ to see popular ways to put objects together into
object models.
Is
there really a situation that the designed class not "derivable"?

std::string is not derivable - it has no virtual methods, so there's no
reason to inherit it. Sometimes people inherit it to form a "convenience
class" that changes its interface a little.

But if we controlled the source to a class we would make it derivable when
we find a reason to.
When should make a member "protected"? Only when allowing the derived
class(es) directly access it?

I never saw a reason for "protected" but there might be one out there.
Disregard it.
Should destructor always be virtual?

Yes. This is a different topic - a Sane Subset. That means you don't write
every possible combination of C++ statements, you only use specific
combinations known to work, and you prefer them to all others. (Read
/Effective C++/ and /Exceptional C++/ to learn what a thin line C++ can
place between bad design and sanity!)

Always use references without a reason to use pointers. Always use local
storage without a reason to use heap storage, etc.

C++ permits bugs in certain situations with non-virtual destructors because
they also optimize certain other situations. So, treat a non-virtual
destructor as the exceptional case, when you need speed, and use virtual
destructors in all other situations.
 
C

Cy Edmunds

alexhong2001 said:
When design a class, should always make it "derivable" as a base class? Is
there really a situation that the designed class not "derivable"?

When should make a member "protected"? Only when allowing the derived
class(es) directly access it?

Should destructor always be virtual?

Thanks for your comments!

Well, your questions cover a lot of territory. I would say that many classes
fall into two broad categories: concrete data types and abstract data types.
(Not everybody uses these names, btw.) Concrete data types have no virtual
methods and are not intended for use as base classes. They are tightly bound
to their implementations. Example: std::complex.

Not all destructors should be virtual. A concrete data type might have a
destructor (e.g. std::vector) but if so it will not be virtual. If you want
to reuse such a class, use encapsulation (aka composition) rather than
inheritance:

class Fred
{
private:
std::vector<double> m_whatever; // good way to reuse std::vector
...
};

NOT

class Fred : public std::vector<double> {...}; // bad way

Abstract data types are used when you have a set or polymorphic classes:

class IShape
{
public:
virtual void draw() const = 0;
virtual ~IShape() {}
};

The typical pattern is no data, no constructor, pure virtual methods, and a
do-nothing virtual destructor. Obviously, such a class is intended as a base
class:

class Circle : public IShape {...};
class Square : public IShape (...};

The point is that if I have an IShape pointer I can draw the shape without
knowing or caring what type it actually is.

There are many other basic designs but those are two I use a lot.

I don't use "protected" very often, but if I do it will contain a method
(not data) which is intended for use by derived classes. Putting data in a
protected area compromises encapsulation IMHO.
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top