views:

247

answers:

6

It took me a long time to realize how important and subtle having variables that:

1) exist on the stack

2) have their destructors called when they fall out of scope

are.

These two things allow things like:

A) RAII

B) refcounted GC

Interesting enough, (1) & (2) are not available in "lower" languages like C/Assembly; nor in "higher" languages like Ruby/Python/Java (since GC prevents predictable destruction of objects).

I'm curious -- what other techniques do you know of that are very C++ specific, due to language design choices.

Thanks!

Edit: If your answer is "this works in C++ & this other langauge", that's okay too. The things I want to learn about are similar to:

By choosing to not have certain features (like GC), we gain other features (like RAII + predicable destructing of objects). In what areas of C++, by choosing to NOT have features that other "higher level" langauges have, C++ manages to get patterns that those higher level langauges can't express.

+1  A: 

Well, this one can be done in the D programming language, as well as the two you mention, but templates powerful enough to essentially function as compile time duck typing are pretty useful. I often feel that a large part of the debate between static and dynamic languages could be resolved by a good enough template system, so that even though types need to be statically resolved at compile time they don't need to be known at development time. C++ pioneered this idea (at least among mainstream languages). D takes it several steps further.

With templates and compile-time duck typing you get the best of both worlds: The flexibility of not having to specify types at development time of a function or class and the safety and performance of knowing them at compile time.

dsimcha
The question is specifically about C++.
anon
@Neil: I'm just pointing out that it's not **completely** specific, though C++ is the most popular language in which this technique is available.
dsimcha
+4  A: 

curiously recurring template pattern

expression templates

Probably the whole meta-programming concept as well

aaa
+2  A: 

Well, almost ALL 'design patterns' have a strong tie to C++. You should rather assume they have NOTHING to do with 'proper design' and 'best practices' and EVERYTHING to do with bizarre C++ foibles and carefully consider the real need for any of them instead of mindlessly complicating your code. Especially since many of the design patterns are bandaids to fix problems created by yet other design patterns. This applies ten times more so when using any language than C++, because C++ has a huge number of problems no other language does.

For example, singleton is the most obvious example of this. The real reason for this is the way that C++ has very poorly implemented static initialization. Without this, you actually have to know what you are doing to make initialization work properly, and most people really don't. For most uses the extra overhead of singleton is fine, but it should be kept in mind that it does have overhead, and it has no use in most languages. It also creates extra problems of in every single implementation I have seen.

Same applies to the bridge pattern, I can see using singletons, but there's simply no reason to ever use bridge pattern. It comes down to 'do you know what you are doing?'. If so, you don't need bridge pattern. If not, you should probably learn more before trying to solve the issue prompting you to use bridge pattern. Most importantly, it is not really useful at all in languages besides C++. The point of it is to separate implementation and interface, which in more modern OO languages is done already because they have proper modules of some kind. In C++, it is better to use pure virtual interfaces for cases like this (and don't think this has worse performance, it has much better performance than bridge pattern combined with templates).

But that is the problem with design patterns; it's not a methodology at all, just a bunch of random things. Random things most of the people advocating them don't really understand, most of which properly have no place being called anything with the word design in them. They are not design tools, but specific solutions to various problems. Many of which are avoidable.

Ironically, java API is full of bridges and a wide variety of other design patterns which are totally pointless in Java. Java API is easy to use most of the time, but the overcomplicated heirarchy can be very cumbersome to work with.

Charles Eli Cheese
Why should a good templated bridge pattern implementation have worse performance then one based on vtables? Also, bridges are not only a question of modules, hiding complexity with a layer-based approach is its main value regardless of language.
Georg Fritzsche
computer science.
Charles Eli Cheese
+1  A: 
  • Always make the caller responsible for memory allocation/deallocation, which means that code that would look like this in Java/C#:

    MyClass doSomething(MyClass someInstance);
    

    looks like this in C/C++:

    void doSomething(MyClass* someBuffer, MyClass* someInstance);
    
  • Using destructors to close sockets, file-handles, etc.
BlueRaja - Danny Pflughoeft
Or `boost::shared_ptr<MyClass> doSomething(const MyClass`
Alex B
If you want the FORCE the caller to be responsible for alloc/free you should use references: void doSomething(const myclass You can't call doSomething with allocating out_param somewhere and passing it in.
jmucchiello
A: 

Few languages support multiple dispatch (with double dispatch being most common) as C++ does, you can do it statically or dynamically.
With it you can let instances interact on each other without yet knowing, or testing for, their concrete type.

Georg Fritzsche
I would say almost ALL languages (except for a few like CLOS that support it natively) support it as C++ does - you have to code it yourself.
anon
C# 4.0 does it!
Hogan
@Hogan: Oh, i have to look that up. Only dynamically though i guess?
Georg Fritzsche
@gf: yeah, see http://blogs.msdn.com/laurionb/archive/2009/08/13/multimethods-in-c-4-0-with-dynamic.aspx
Hogan
+2  A: 

I really love trait classes. Not exactly specific of C++ (other languages as Scala have them), but it allows you to adapt objects, basically to specify a set of operations that a type should support. Imagine that you want a "hasher", in the sense of tr1::hash. hash is defined for some types, but not for others. How can you make a class that has a hash defined for all the types that you want? You can declare a class such as:

template < typename T>
struct hashing_tratis
{
    typedef std::tr1::hash<T> hashing_type;
};

that is, you expect a class that has the correct hasing_type defined to be used. However, hash is not defined, say, for myType, so you can write:

template <>
struct hashing_traits<myType>
{
    typedef class_that_knows_how_to_hash_myType hashing_type;
};

This way, suppose that you need a way to hash any type that you use in your program (including myType). You can write an "universal" hasher by creating a hasing trait:

template <typename T>
struct something {
    typename hashing_traits<T>::hashing_type hasher;
    .... // here hasher is defined for all your relevant types, and knows how to hash them
Diego Sevilla
Languages that support reflection can do this without the cumbersome traits class. They just ask the class if it supports an interface and calls it. The same is also possible in languages with first order function support.
jmucchiello
jmucchiello: Well, not exactly. What happens, for example, when you ask for a class or interface that this object does not implement? You have to "search" for an adaptator. Type parameterization in C++ allows this trait construction with a homogeneous interface.
Diego Sevilla