How to learn the C++ way?

  • Follow


Dear newsgroup readers,

When learning C++ I also studied Design Patterns because I figured that
if C++ is an object oriented language I better start thinking the
OO-way.

I soon discovered that C++ really is a combination of OO, functional
and generic programming. So I tried to expand my knowledge of these
paradigms by learning other programming languages. And indeed learning
Haskell really deepened my understanding of the STL (but I really miss
lambda expressions now). Learning Ruby opened my eyes to the usefulness
of iterators and each-loops (but I really miss closures now in C++).

When programming in C++ I see now many more ways to write my code and I
am left confused on how to proceed. With all these options available,
what is the C++ way?

Please share me your insights.

Kind regards,
Francis


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply francis.rammeloo (37) 7/28/2006 12:04:47 PM

francis_r wrote:

> When programming in C++ I see now many more ways to write my code and I
> am left confused on how to proceed. With all these options available,
> what is the C++ way?

well in some stituations one or other technique usually stands out as
the way to do it, so you just rust your judgement and do it that way.
the tricky thing to realise is; if all options seem equally good, then
your intuition is probably right here too and any of them will do, so
just pick one (perhaps based on similarity to other parts of design)
and stop worrying about it. if all options seem equally bad thaen thats
usually a sign that you are missing something (either a pattern/idion
or perhaps something in the design).


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply jk 7/28/2006 1:18:33 PM


"francis_r" <francis.rammeloo@gmail.com> wrote in 
news:1154081437.672662.56150@m79g2000cwm.googlegroups.com:

> Dear newsgroup readers,
> 
> When learning C++ I also studied Design Patterns because I figured that
> if C++ is an object oriented language I better start thinking the
> OO-way.

The Design Patterns book is a classic and fundamental for learning
good design and C++ development.  Given its age, I would caution that
the example implementations do not represent best or good practices in
many current environments.  For example, compare its Singleton with
that of Loki's.

> I soon discovered that C++ really is a combination of OO, functional
> and generic programming. So I tried to expand my knowledge of these
> paradigms by learning other programming languages. And indeed learning
> Haskell really deepened my understanding of the STL (but I really miss
> lambda expressions now).

Check out Boost Lambda.

>Learning Ruby opened my eyes to the usefulness
> of iterators and each-loops (but I really miss closures now in C++).
>
> When programming in C++ I see now many more ways to write my code and I
> am left confused on how to proceed. With all these options available,
> what is the C++ way?

It's a balance between the needs of your project and best practices
for your problem domain.  Suppose you're developing a three-tiered
application, involving data, business logic, and presentation (such as
a typical web application).  OO is a good way to implement the domain
objects in the business layer.  The classes are easy to model and
translate into code.  If there is commonality of operations, you might
choose some generic techniques (if nothing else, by using STL).
Hopefully, the data/business mapping layer can easily be mapped
orthogonally with a third-party mapper/serializer.  A functional
approach could be best for the mapping because each mapping could be
represented as a transform.  Likewise, some of the user-interface
presentation would be transform-dependent.  On the other hand a
model-view-controller (MVC) framework has many OO aspects in its
implementation and interface.

Leveraging these techniques where appropriate is sound engineering.
For example, if you were to avoid generic practices for containers,
then you'd be stuck with something like pre-Java generics for storing
a collection of objects.  Perhaps, everything would have to inherit
from a common base class to be stored.  If you try to use certain
generic algorithms without functional Boost Lambda (or C++ TR1's
implementation), you'd have to write ad-hoc functions to apply
transforms across objects that don't quite fit the interface.  If you
implement your domain objects using a functional technique, you really
wouldn't have a three-tiered architecture anymore.

Some of the techniques are cutting edge, such as Boost Lambda.
Therefore, you need to balance whether your platform and those looking
at the code can support them.  If you're building a GUI on a Microsoft
platform, and you have a requirement to support MFC instead of ATL,
you're stuck with its view of the world, circa 1997.  No matter what
understand the current best practices because they can help you avoid
things like thread-unsafe singletons and brittle state machines.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply W 7/28/2006 2:34:30 PM

Thank you for sharing, I have learned from this post.

W. J. La Cholter wrote:

> For example, if you were to avoid generic practices for containers,
> then you'd be stuck with something like pre-Java generics for storing
> a collection of objects.  Perhaps, everything would have to inherit
> from a common base class to be stored.  If you try to use certain
> generic algorithms without functional Boost Lambda (or C++ TR1's
> implementation), you'd have to write ad-hoc functions to apply
> transforms across objects that don't quite fit the interface.  If you
> implement your domain objects using a functional technique, you really
> wouldn't have a three-tiered architecture anymore.
>
This part is very interesting to me. I still have argument with java
people about why is STL better than the pre-Java generics collection in
Java. As you say, everything must inherit from a base in order to be in
the collection. I know this is bad. Can you break it down for
beginner/intermediate like me and others I argue with? Here is some
pseudo-code (and C++ used to be like this pre-STL remember RougeWave
RWCollection?). Why is line 25 so bad? Why forcing inheritance from
Collectable is so bad?

1  // all must inherit from here to be in a Collection
2  class Collectable
3  {
4    virtual int comp(const Collectable *, const Collectable *) = 0;
5  };
6  class Collection
7  {
8  public:
9  void add(Collectable *);
10 size_t size();
11 Collectable *getAt(size_t);
12 };
13 class Canvas; // forward declaration
14 class Shape: public Collectable
15 {
16 public:
17 virtual void draw(Canvas &) = 0;
18
19 };
20 class Canvas
21 {
22 public:
23 void render(Collection &sc) {
24 for ( size_t i = 0; i < sc.size(); i++ )
25 ((Shape *)sc.getAt(i))->draw(*this);
26 }
27 };
28 class Rectangle: public Shape
29 {
30 public:
31 void draw(Canvas &) {;} // override to draw rectangle
32 };
33 class Triangle: public Shape
34 {
35 public:
36 void draw(Canvas &) {;} // override to draw Triangle
37 };
38

I have some reasons, but I could benefit from more detail and stronger
reasons.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply moleskyca1 7/28/2006 9:09:43 PM

<moleskyca1@yahoo.com> wrote:

> > Thank you for sharing, I have learned from this post.
> >
> > W. J. La Cholter wrote:
> >
>> > > For example, if you were to avoid generic practices for containers,
>> > > then you'd be stuck with something like pre-Java generics for 
storing
>> > > a collection of objects.  Perhaps, everything would have to inherit
>> > > from a common base class to be stored.  If you try to use certain
>> > > generic algorithms without functional Boost Lambda (or C++ TR1's
>> > > implementation), you'd have to write ad-hoc functions to apply
>> > > transforms across objects that don't quite fit the interface. 
If you
>> > > implement your domain objects using a functional technique, you 
really
>> > > wouldn't have a three-tiered architecture anymore.
>> > >
> > This part is very interesting to me. I still have argument with java
> > people about why is STL better than the pre-Java generics collection in
> > Java. As you say, everything must inherit from a base in order to be in
> > the collection. I know this is bad. Can you break it down for
> > beginner/intermediate like me and others I argue with? Here is some
> > pseudo-code (and C++ used to be like this pre-STL remember RougeWave
> > RWCollection?). Why is line 25 so bad? Why forcing inheritance from
> > Collectable is so bad?
> > [code snipped]

> > I have some reasons, but I could benefit from more detail and stronger
> > reasons.
> >
   It looks like you want a container of polymorphic Shape's. That said
I created a base class suitable for boost::intrusive_ptr as I have an
intruxive ptr with the same interface as boost's on this old compiler.
// Shapes.h
// forward declaration ok since we only use Canvas & in this header.
class Canvas;

class Shape
{
         long count;  // counter for reference counting
protected:
         Shape(){}  // prevent direct construction
public:
         virtual void Draw(Canvas &) = 0;
         virtual ~Shape(){} // so derived class's dtor is called
         // functions for intrusive_ptr
         friend void intrusive_ptr_add_ref(Shape *p) {++(p->count);}
         friend void intrusive_ptr_release(Shape *p)
         {
                 if(!--(p->count)) delete p;
         }
};

class Rectangle:public Shape
{
public:
         void Draw(Canvas &);
};


class Triangle:public Shape
{
public:
         void Draw(Canvas &);
};

class Circle:public Shape
{
public:
         void Draw(Canvas &);
};
// Collection.h
#include <list>  // for a list container vector or deque also work.
#include "intrusive_ptr.h" // boost/intrusive_ptr.hpp in effect
#include "Shapes.h"

// namespace mine = boost;

typedef std::list< mine::intrusive_ptr<Shape> > Collection;

class Canvas
{
// simplest on this old compiler, various libs allow 'inline'
// creation of this
         class render_one
         {
                 Canvas &canvas;
         public:
                 render_one(Canvas &a):canvas(a){}
                 void operator () (mine::intrusive_ptr<Shape> x)
                 {
                         x->Draw(canvas);
                 }
         };
public:
         void render(Collection &sc)
         {
// walk the list
                 std::for_each
                 (
                         sc.begin(),
                         sc.end(),
                         render_one(*this)
                 );
         }
};

this looks right and works if draw writes class name to std::cout.
[driver and the implementations not included above]

This looks like a safe implementation of a list of polymorphic objects.
and is probably more efficient than an imitation of JAVA code in C++.


I apologize if this was already in this thread...

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply cbarron3 7/30/2006 5:00:36 PM

moleskyca1@yahoo.com wrote in news:1154120006.944830.195500
@m73g2000cwd.googlegroups.com:

> 
> Thank you for sharing, I have learned from this post.
> 
> W. J. La Cholter wrote:
> 
>> For example, if you were to avoid generic practices for containers,
>> then you'd be stuck with something like pre-Java generics for storing
>> a collection of objects.  Perhaps, everything would have to inherit
>> from a common base class to be stored.  If you try to use certain
>> generic algorithms without functional Boost Lambda (or C++ TR1's
>> implementation), you'd have to write ad-hoc functions to apply
>> transforms across objects that don't quite fit the interface.  If you
>> implement your domain objects using a functional technique, you really
>> wouldn't have a three-tiered architecture anymore.
>>
> This part is very interesting to me. I still have argument with java
> people about why is STL better than the pre-Java generics collection in
> Java. As you say, everything must inherit from a base in order to be in
> the collection. I know this is bad. Can you break it down for
> beginner/intermediate like me and others I argue with? Here is some
> pseudo-code (and C++ used to be like this pre-STL remember RougeWave
> RWCollection?). Why is line 25 so bad? Why forcing inheritance from
> Collectable is so bad?
> 
> 1  // all must inherit from here to be in a Collection
> 2  class Collectable
> 3  {
> 4    virtual int comp(const Collectable *, const Collectable *) = 0;
> 5  };
> 6  class Collection
> 7  {
> 8  public:
> 9  void add(Collectable *);
> 10 size_t size();
> 11 Collectable *getAt(size_t);
> 12 };
> 13 class Canvas; // forward declaration
> 14 class Shape: public Collectable
> 15 {
> 16 public:
> 17 virtual void draw(Canvas &) = 0;
> 18
> 19 };
> 20 class Canvas
> 21 {
> 22 public:
> 23 void render(Collection &sc) {
> 24 for ( size_t i = 0; i < sc.size(); i++ )
> 25 ((Shape *)sc.getAt(i))->draw(*this);

The immediate answer is that the operation can fail for many different
reasons.  There's nothing stopping the collection from holding
different types.  In C++, without native garbage collection, you're
force to manage the item's destruction.  You've also cluttered your
class hierarchy.  If you wish to have a Shape play with other other
things, you need to add interfaces.  Sun can get around this by
modifying java.lang.Object when they have the need to support new
features.

The theoretical answer is that Collection doesn't represent what you
want it to.  It represents a collection of anything.  Presumably, you
just want a collection of shapes.  That could be represented as
std::vector<boost::shared_ptr<Shape> >.  The shared_ptr is a safe way
to manage the object lifetime.  If you want a collection of anything,
you could use std::vector<boost::any>.

> 26 }
> 27 };
> 28 class Rectangle: public Shape
> 29 {
> 30 public:
> 31 void draw(Canvas &) {;} // override to draw rectangle
> 32 };
> 33 class Triangle: public Shape
> 34 {
> 35 public:
> 36 void draw(Canvas &) {;} // override to draw Triangle
> 37 };
> 38

C++ because it's multi-paradigm, is well suited for "say what you
mean" and "mean what you say."  You can represent or model your
relationships, and clearly transform them into code.  Being
"collectable" is orthogonal, or separate from a shape's having other
properties.  Moreover, later on, if you wish to add more properties,
such as Renderable or Printable, you'd need have to hope the existing
interface suits your needs or you'd have to add an interface.

An interesting solution adopted in Java is the use of reflection to
implement orthogonal things like serialization or object-relational
mapping.  In the old way, to make something persistent, you'd have it
derive from something like "Persistible."  A better solution is
something like Hibernate, in which the bindings are defined
declaratively at run-time.  That's preferable to mucking up a class
hierarchy, but injects dynamism that may not be necessary.

Another limitation to the above approach is that you cannot implement
something like the Non-Virtual Interface idiom (see other posts in the
group or C++ Coding Standards by Sutter and Alexandrescu): the
interface is public and the implementation uses private virtual
functions.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Reply W 7/31/2006 8:46:57 PM

5 Replies
73 Views

(page loaded in 0.234 seconds)

Similiar Articles:













7/14/2012 3:04:01 PM


Reply: