17

After discussing with a newly arrived developer in my team, I realized that there are still, in C++, habits of using C constructs because they are supposed to be better (i.e. faster, leaner, prettier, pick your reason).

What are the examples worth sharing, showing a C constructs, compared to the similar C++ construct?

For each example, I need to read the reasons the C++ construct is as good as or even better the original C construct. The aim is to offer alternatives to some C constructs that are considered somewhat dangerous/unsafe in C++ code (C++0x valid only answers are accepted as long as clearly marked as C++0x only).

I'll post below an answer (struct inline initialization) as an example.

Note 1: Please, one answer per case. If you have multiple cases, please post multiple answers

Note 2: This is not a C question. Do not add the "C" tag to this question. This is not supposed to become a fight between C++ and C. Only the study of some constructs of the C subset of C++, and their alternative in other C++ "toolkits"

Note 3: This is not a C-bashing question. I want reasons. Boasting, bashing, and unproven comparisons will be downmodded. Mentioning C++ features without a C equivalent could be considered out of topic: I want the put side by side a C feature against a C++ feature.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
paercebal
  • 81,378
  • 38
  • 130
  • 159
  • Why not add the C tag? This question, while not directly about C, is certainly relevant to people interested in C. – Adam Rosenfield Oct 22 '08 at 18:58
  • I agree with Adam about the C tag. – Michael Burr Oct 22 '08 at 19:03
  • 1
    My initial reasons were that I did not want a C++ vs C bashing fest. This is not an attempt to "convert", and this is not an attempt to boast one language against the other. I just did not want to have a "fight"... Still, you're perhaps right. – paercebal Oct 22 '08 at 19:44
  • If there's no attempt to convert, why does the C++ construct a priori have to be 'as good as or even better'? – fizzer Oct 22 '08 at 19:48
  • Because when C is better and not dangerous, it remains a good alternative for the developer. So, I'm only interested in the cases when C is *not* better. – paercebal Oct 22 '08 at 20:02
  • I added the "c" tag, after the comments of Adam and Mike. Thanks for the input. – paercebal Oct 22 '08 at 20:05
  • Do we have a 'propaganda' tag? I won't be contributing to this thread any more - the game is rigged. – fizzer Oct 22 '08 at 20:11
  • @fizzer.myopenid.com, I don't think he's saying that C++ is _always_ better. I think he's looking for examples of cases where C++ is better. I know more than one developer who still likes C better than C++ because C++ can be more tricky to debug due to operator overloading and issues of that sort. – Onorio Catenacci Oct 22 '08 at 22:00
  • @Onorio: I guess there was a misunderstanding: Despite what his comment (probably written in haste) could let believe, Fizzer contributed positively to the thread and showed a good understanding of C++. My own knowledge of C++ increased because of the discussion we had. – paercebal Oct 22 '08 at 22:15
  • @Onorio: [...] But you did plainly described my viewpoint, probably better than I did in my posts. Thanks. – paercebal Oct 22 '08 at 22:16
  • Marked as poll because multiple answers are expected, not one. – Suma Oct 23 '08 at 11:13

20 Answers20

34

RAII and all the ensuing glory vs. manual resource acquisition/release

In C:

Resource r;
r = Acquire(...);

... Code that uses r ...

Release(r);

where as examples, Resource could be a pointer to memory and Acquire/Release will allocate/free that memory, or it could be an open file descriptor where Acquire/Release will open/close that file.

This presents a number of problems:

  1. You might forget to call Release
  2. No information about the data flow for r is conveyed by the code. If r is acquired and released within the same scope, the code does not self-document this.
  3. During the time between Resource r and r.Acquire(...), r is actually accessible, despite being uninitialized. This is a source of bugs.

Applying the RAII (Resource Acquisition Is Initialization) methodology, in C++ we obtain

class ResourceRAII
{
  Resource rawResource;

  public:
  ResourceRAII(...) {rawResource = Acquire(...);}
  ~ResourceRAII() {Release(rawResource);}

  // Functions for manipulating the resource
};

...

{
  ResourceRAII r(...);

  ... Code that uses r ...
}

The C++ version will ensure you do not forget to release the resource (if you do, you have a memory leak, which is more easily detected by debugging tools). It forces the programmer to be explicit about how the resource's data flow (ie: if it only exists during a function's scope, this would be made clear by a construction of ResourceRAII on the stack). There is no point during between the creation of the resource object and its destruction where the resource is invalid.

Its also exception safe!

s4y
  • 50,525
  • 12
  • 70
  • 98
Andrew Top
  • 2,517
  • 1
  • 17
  • 10
  • I believe in your example, you need to adjust the constructor and destructor to be named ResourceRAII() and ~ResourceRAII(), as that is the name of the class. – Aaron Dec 31 '08 at 02:31
  • 1
    +1, RAII is the best C++ invention ever. I did not even found a single case where it was "over-used" (like e.g. a singleton pattern). Its just the best thing since sliced bread, and if would've been invented earlier, maybe nobody would've had to invent garbage collection ... – Frunsi Jan 11 '10 at 03:47
27

Macros vs. inline templates

C style:

#define max(x,y) (x) > (y) ? (x) : (y)

C++ style

inline template<typename T>
const T& max(const T& x, const T& y)
{
   return x > y ? x : y;
}

Reason to prefer C++ approach:

  • Type safety -- Enforces that arguments must be of same type
  • Syntax errors in definition of max will point to the correct place, rather than where you call the macro
  • Can debug into the function
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
JohnMcG
  • 8,709
  • 6
  • 42
  • 49
  • In addition, the C++ version doesn't evaluate arguments twice. Consider max(++i, ++j): the C version will increment either i or j twice rather than once. – David Thornley Oct 22 '08 at 18:49
  • Perhaps a better version would be two "max" functions. One waiting for const reference parameters and returning the const reference of the max, the other waiting for non-const reference parameters, and returning the non-const reference of the max... But this is out topic ^_^ ... +1 – paercebal Oct 22 '08 at 18:52
  • 4
    Max really isn't that simple, you really should read this http://www.ddj.com/cpp/184403774 – Motti Oct 22 '08 at 19:00
  • Right -- I remember reading that when it came out -- my version also makes a copy, which is probably undesirable and I will edit to fix. – JohnMcG Oct 22 '08 at 19:15
  • The C version is wrong, dangerous, or both. I'll add the parentheses necessary to make it safe(r). – Konrad Rudolph Oct 22 '08 at 20:03
  • You've still got the C version wrong. parens around the entire expansion are needed as well. – Roddy Oct 22 '08 at 20:33
  • The C version may be dangerous, but I still use it in preference to "80 lines of template source code (plus the Loki library) which won't compile with any currently available compiler". (summary of motti's link) – Roddy Oct 22 '08 at 20:45
  • 1
    @Roddy: JohnMcG's solution, completed with the non-const version, is more than enough for most uses. I've yet to be bothered by std::min and std::max offered by the STL (use the one line "#include "), while a "#define max" macro already trashed a perfectly namespaced/encapsulated code. – paercebal Oct 22 '08 at 21:59
  • Of coutse, that the macro version is complicated to get right is part of the problem with it. – JohnMcG Oct 23 '08 at 00:12
  • Not withstanding that Macros have a 'scope' from `#define` to `#undef` or the end of the file. `template<>`s are scoped by namespaces, classes, functions, and blocks, just as other function and class names are. – Aaron Dec 31 '08 at 02:37
  • It took me a few tries, but i hope it's fine now: http://codepad.org/R6i0te8h . tries to work with const/nonconst parameters, and with them having different types (finds their common type by applying of operator ?: ) no loki or whatsoever needed. but maybe there is a bug i've overseen? – Johannes Schaub - litb Jan 03 '09 at 16:58
18

Dynamic arrays vs. STL containers

C-style:

int **foo = new int*[n];
for (int x = 0; x < n; ++x) foo[x] = new int[m];
// (...)
for (int x = 0; x < n; ++x) delete[] foo[x];
delete[] foo;

C++-style:

std::vector< std::vector<int> > foo(n, std::vector<int>(m));
// (...)

Why STL containers are better:

  • They're resizeable, arrays have fixed size
  • They're exception safe - if unhandled exception occurs in (...) part, then array memory may leak - container is created on stack, so it will be destroyed properly during unwind
  • They implement bound checking, e.g. vector::at() (getting out of bounds on array will most likely generate an Access Violation and terminate the program)
  • They're easier to use, e.g. vector::clear() vs. manually clearing the array
  • They hide memory management details, making code more readable
Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • I guess there is no need for a 2D array. A 2D array for C++ could be broken (i.e. one line could be 5 items long, and the other could 7). Whereas a char a[25][6] is always Ok and fast. The same example with a 1D array vs. a vector would be better. Until we have N-dimension arrays in C++ – paercebal Oct 22 '08 at 19:56
  • Note, too, that the access through vector::operator[] is as fast as the one one a C array through pointer arithmetics or [] use. – paercebal Oct 22 '08 at 19:56
  • P.S.: When I write "A 2D array for C++ could be broken", I write about an exposed vector of vector. A private vector of vector in a "Matrix" class would not be victim of this problem thanks to encapsulation. But this goes beyond the topic, I guess. – paercebal Oct 22 '08 at 21:41
17

#define vs. const

I keep seeing code like this from developers who have coded C for a long time:

#define MYBUFSIZE 256

.  .  . 

char somestring[MYBUFSIZE];

etc. etc.

In C++ this would be better as:

const int MYBUFSIZE = 256;

char somestring[MYBUFSIZE];

Of course, better yet would be for a developer to use std::string instead of a char array but that's a separate issue.

The problems with C macros are legion--no type checking being the major issue in this case.

From what I've seen, this seems to be an extremely hard habit for C programmers converting to C++ to break.

Onorio Catenacci
  • 14,928
  • 14
  • 81
  • 132
  • This used to be awkward because compilers/linkers wouldn't let you place "const int X=100" in the .h file - you needed "extern const int X" in a header, and "const int x=100;" in a .c file, which was painful. Seems to work OK now, though. – Roddy Oct 22 '08 at 20:48
  • @Roddy : Today, this is ok for const expressions (I guess they will be inlined away) – paercebal Oct 22 '08 at 21:40
  • I've actually grown to like using enums for constants - works well in both C and C++. The only drawback is that you don't get to force a particular type, but that's rarely a concern. – Michael Burr Oct 23 '08 at 07:35
  • One big problem with macros like this is that they are global. What if two unrelated libraries define "BUFSIZE"? – Qwertie Dec 24 '08 at 19:33
  • @Qwertie : To be more clear, Macros have a 'scope' from `#define` to `#undef` or the end of the file. `const`s are scoped by namespaces, classes, functions, and blocks, just as variables. – Aaron Dec 31 '08 at 02:36
  • 1
    I like to use "static const int x = 100;" in the header files. Each translation unit will then technically have it's own copy of the constant, but as paercebal points out, you can expect it to be inlined away. – Boojum Dec 31 '08 at 03:43
  • In the Microsoft debugger, putting your cursor over the `const int` defined label will display the value, but not for labels using `#define`. This is enough reason for me to prefer it. – Mark Ransom Oct 07 '10 at 21:40
14

Default parameters:

C:

void AddUser(LPCSTR lpcstrName, int iAge, const char *lpcstrAddress);
void AddUserByNameOnly(LPCSTR lpcstrName)
  {
  AddUser(lpcstrName, -1,NULL);
  }

C++ replacement/equivalent:

void User::Add(LPCSTR lpcstrName, int iAge=-1, const char *lpcstrAddress=NULL);

Why it's an improvement:

Allows programmer to write express the function of the program in fewer lines of source code and in a more compact form. Also permits default values for unused parameters to be expressed closest to where they are actually used. For the caller, simplifies the interface to the class/struct.

Tobi
  • 78,067
  • 5
  • 32
  • 37
Steve
  • 1,065
  • 15
  • 34
  • I don't really like that use of overloaded functions. Yes, they all add info, but I don't think they should be one function that's overloaded. – Thomas Owens Oct 22 '08 at 19:06
  • I reformatted the post to have the code pretty-printed. Please Steve, could you edit your post to break it into two posts, and explain the pros/cons of each solution? (And change, perhaps, the LPCSTR into const char * for the non-Windows people?). I'm waiting for that to add a +1 to each post. – paercebal Oct 22 '08 at 19:35
  • @Thomas: I agree, even if it still depends on the context. While the example can be discussed, their comparison with the C code is what gives them their value in the current topic. I'm surprised no one mentionned the "abs" and "fabs" of C against a possible cpp::abs function. – paercebal Oct 22 '08 at 19:40
  • @paercebal. Thanks for the edit and the suggestions. I have done as you requested. – Steve Oct 24 '08 at 16:13
  • Default parameters are very useful, but one must be careful with them. I've seen them pretty badly abused ("magic" default values that drastically change the function's behaviour, for instance.) – Jonathan Grynspan Oct 07 '10 at 21:07
13

C's qsort function versus C++' sort function template. The latter offers type safety through templates which have obvious and less obvious consequences:

  • Type safety makes the code less error-prone.
  • The interface of sort is slightly easier (no need to specify the size of the elements).
  • The compiler knows the type of the comparer function. If, instead of a function pointer, the user passes a function object, sort will perform faster than qsort because inlining the comparison becomes trivial. This isn't the case with function pointers that are necessary in the C version.

The following example demonstrates the usage of qsort versus sort on a C-style array of int.

int pint_less_than(void const* pa, void const* pb) {
    return *static_cast<int const*>(pa) - *static_cast<int const*>(pb);
}

struct greater_than {
    bool operator ()(int a, int b) {
        return a > b;
    }
};

template <std::size_t Size>
void print(int (&arr)[Size]) {
    std::copy(arr, arr + Size, std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
}

int main() {
    std::size_t const size = 5;
    int values[] = { 4, 3, 6, 8, 2 };

    { // qsort
        int arr[size];
        std::copy(values, values + size, arr);
        std::qsort(arr, size, sizeof(int), &pint_less_than);
        print(arr);
    }

    { // sort
        int arr[size];
        std::copy(values, values + size, arr);
        std::sort(arr, arr + size);
        print(arr);
    }

    { // sort with custom comparer
        int arr[size];
        std::copy(values, values + size, arr);
        std::sort(arr, arr + size, greater_than());
        print(arr);
    }
}
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Please, could you add code as example: One use of qsort, and one use of std::sort? – paercebal Oct 22 '08 at 20:59
  • 1
    Note that Bjarne Stroustrup confirms Konrad's answer: http://www.research.att.com/~bs/bs_faq2.html#sort – paercebal Oct 22 '08 at 21:01
  • I believe I read somewhere C got better at inlining call to function through function pointers, but I have still to find that article. Anyway, the point is more than valid. – paercebal Oct 22 '08 at 21:04
8

struct inline initialization vs. inline constructors

Sometimes, we need in C++ a simple aggregation of data. The data being somewhat independant, protecting it through encapsulation would not be worth the effort.

// C-like code in C++
struct CRect
{
   int x ;
   int y ;
} ;

void doSomething()
{
   CRect r0 ;               // uninitialized
   CRect r1 = { 25, 40 } ;  // vulnerable to some silent struct reordering,
                            // or adding a parameter
}

; I see three problems with the code above:

  • if the object is not specifically initialized, it won't be at initialized all
  • if we echange x or y (for whatever reason), the default C initialization in doSomething() will now be wrong
  • if we add a z member, and liked it to be "zero" by default, we would still need to change every inline initializing

The code below will have the constructors inlined (if really useful), and thus, will have a zero cost (as the C code above):

// C++
struct CRect
{
   CRect() : x(0), y(0) {} ;
   CRect(int X, int Y) : x(X), y(Y) {} ;
   int x ;
   int y ;
} ;

void doSomething()
{
   CRect r0 ;
   CRect r1(25, 40) ;
}

(The bonus is that we could add a operator== methods, but this bonus is out of topic, and so worth mentioning but not worth as an answer.)

Edit: C99 has named initialized

Adam Rosenfield made an interesting comment I find very interesting:

C99 allows named initializers: CRect r = { .x = 25, .y = 40 }

This won't compile in C++. I guess this should be added to C++, if only for C-compatibiliy. Anyway, in C, it alleviates the problem mentioned in this answer.

Community
  • 1
  • 1
paercebal
  • 81,378
  • 38
  • 130
  • 159
  • C99 allows named initializers: CRect r = { .x = 25, .y = 40 }; – Adam Rosenfield Oct 22 '08 at 18:57
  • Interesting point. This won't compile in C++ g++ ("error: expected primary-expression before ‘.’ token"), so this does not invalidate this whole post, but still a very good point worth knowing. – paercebal Oct 22 '08 at 20:42
  • Nope, it doesn't compile with g++, but if you're compiling _pure_ C code, it works with gcc. I've tested it with gcc 3.4.4, but I'm sure it works in plenty of other versions of gcc as well. – Adam Rosenfield Oct 22 '08 at 20:53
  • 1
    I don't see why inline initialisation is "vulnerable" to adding or re-ordeing members, but calling a constructor apparently isn't "vulnerable" to adding or re-ordering parameters. Either an interface is fixed or it isn't, and that applies to a struct as much as it does to a constructor. – Steve Jessop Oct 22 '08 at 21:20
  • Actually, I'll give you "adding a parameter", since if you add a parameter to the constructor, the code will stop compiling, whereas if you add a member to a struct then the leftovers are silently 0-inited. But they're equally vulnerable to re-ordering. – Steve Jessop Oct 22 '08 at 21:21
  • @onebyone: Take the two CRect structs, compile them, and see the result. Then invert the int x/int y declaration inside the struct. You'll see the C CRect is inverted, while the C++ CRect has still the correct result. [...] – paercebal Oct 22 '08 at 21:48
  • 1
    [...] The "interface" of the C++ CRect is the constructor (as long as the constructor is unchanged, everything's ok), while the interface of the C CRect is the struct itself (the moment it's changed, it won't work). The zero-cost inline C++ indirection given by the constructor changes everything. – paercebal Oct 22 '08 at 21:49
  • I agree with this post, but wanted to point out that adding a constructor to a struct will make it no longer a POD type. This is not the case in C++0x – mmocny Nov 14 '08 at 15:13
  • @michalmocny: You're right, but the "POD" is a non issue on C++ (i.e. who cares?) unless you're playing with shared memory between processes, malloc-allocated memory or other pure-C-features. In this post, I was only mentionning C-style constructs against C++ constructs in pure C++ compiled code. – paercebal Nov 30 '08 at 20:34
7

iostream vs stdio.h

In C:

#include <stdio.h>

int main()
{
    int num = 42;

    printf("%s%d%c", "Hello World\n", num, '\n');

    return 0;
}

The format string is parsed at runtime which means it is not type safe.

in C++:

#include <iostream>

int main()
{
    int num = 42;

    std::cout << "Hello World\n" << num << '\n';
}

The data types are known at compile time, and there's also less to type because there is not need for a format string.

Jeff Linahan
  • 3,775
  • 5
  • 37
  • 56
  • Have you ever profiled stdio vs. iostream? I think you'll be surprised to find that stdio is significantly faster than iostream. – Adam Rosenfield Oct 22 '08 at 19:38
  • I agree. While this is interesting (sprintf is easy to abuse), iostreams are slow enough (i.e. 5 times stdio equivalent if my memory serves) to have Herb Sutter write an article on it. [...] – paercebal Oct 22 '08 at 19:59
  • [...] Thus, this answer is outside my "I need to read the reasons the C++ construct is as good as or even better the original C construct" requirement. – paercebal Oct 22 '08 at 20:00
  • 1
    printf (besides being faster) also is much easier to type in. All the closing and reopening quotes (for cout) really slows you down. – paperhorse Oct 22 '08 at 20:03
  • Yep. There's also the question of verbosity - setw, setprecision and friends versus the compact field width and precision modifiers for % conversions. – fizzer Oct 22 '08 at 20:03
  • "less to type" seems to be a spurious claim too, given that in the above example, the iostream code would be precisely the same number of characters, except that the "return 0" from main was left out... – Steve Jessop Oct 22 '08 at 21:15
  • The verbosity of iostream drives me mental and I refuse to use it in anything but the most trivial cases. To wit: printf("0x%08xn", x); vs std::cout << std::hex << std::setfill('0') << std::setw(8) << x << std::dec << std::endl; – Rodyland Oct 22 '08 at 21:17
  • I agree with all the posts about iostream's verbosity. C++ iostreams are safer than any C counterpart (the result will always be Ok, i.e., no buffer overrun and no truncated string), but it is more verbose and slower. I guess this should be mentionned in the answer. – paercebal Oct 22 '08 at 21:44
  • (sn)printf is also better for i18n, at least with gettext, because the translator gets the whole string and not only some snippets of it. He can even reorder the values if needed. – quinmars Oct 23 '08 at 08:57
  • I have to admit, I've always found format strings pretty confusing, primarily because I've never really used C-style IO. Having to specify whether I'm printing a `double` or an `int` seems redundant and error-prone, especially in templates. As for the performance issues, my understanding is that most of the performance gap is caused by people using `std::endl` for newline characters rather than just `'\n'`, and the rest can be fixed by using `std::cout.sync_with_stdio(false)`. See the timings in the answer to http://stackoverflow.com/questions/1924530/mixing-cout-and-printf-for-faster-output – David Stone Aug 28 '12 at 04:58
5

Following fizzer's post at C++ constructs replacing C constructs, I'll write here my answer:

Warning: The C++ solution proposed below is not standard C++, but is an extension to g++ and Visual C++, and is proposed as a standard for C++0x (Thanks to Fizzer's comments about this)

Note that Johannes Schaub - litb's answer offers another, C++03 compliant way to do it anyway.

Question

How to extract the size of a C array?

Proposed C solution

Source: When are C++ macros beneficial?


#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])

Unlike the 'preferred' template solution discussed in a current thread, you can use it as a constant expression:

char src[23];
int dest[ARRAY_SIZE(src)];

I disagree with Fizzer as there is a templated solution able to generate a constant expression (In fact, a very interesting part of templates is their capacity to generate constant expressions at compilation)

Anyway, ARRAY_SIZE is a macro able to extract the size of a C array. I won't elaborate about the macros in C++: The aim is to find an equal or better C++ solution.

A better C++ solution?

The following C++ version has none of the macro problems, and can do anything the same way:

template <typename T, size_t size>
inline size_t array_size(T (&p)[size])
{
   // return sizeof(p)/sizeof(p[0]) ;
   return size ; // corrected after Konrad Rudolph's comment.
}

demonstration

As demonstrated by the following code:

#include <iostream>

// C-like macro
#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])

// C++ replacement
template <typename T, size_t size>
inline size_t array_size(T (&p)[size])
{
   // return sizeof(p)/sizeof(p[0]) ;
   return size ; // corrected after Konrad Rudolph's comment.
}

int main(int argc, char **argv)
{
   char src[23];
   char * src2 = new char[23] ;
   int dest[ARRAY_SIZE(src)];
   int dest2[array_size(src)];

   std::cout << "ARRAY_SIZE(src)  : " << ARRAY_SIZE(src) << std::endl ;
   std::cout << "array_size(src)  : " << array_size(src) << std::endl ;
   std::cout << "ARRAY_SIZE(src2) : " << ARRAY_SIZE(src2) << std::endl ;
   // The next line won't compile
   //std::cout << "array_size(src2) : " << array_size(src2) << std::endl ;

   return 0;
}

This will output:

ARRAY_SIZE(src)  : 23
array_size(src)  : 23
ARRAY_SIZE(src2) : 4

In the code above, the macro mistook a pointer for an array, and thus, returned a wrong value (4, instead of 23). The template, instead, refused to compile:

/main.cpp|539|error: no matching function for call to ‘array_size(char*&)’|

Thus demonstrating that the template solution is: * able to generate constant expression at compile time * able to stop the compilation if used in the wrong way

Conclusion

Thus, all in all, the arguments for the template is:

  • no macro-like pollution of code
  • can be hidden inside a namespace
  • can protect from wrong type evaluation (a pointer to memory is not an array)

Note: Thanks for Microsoft implementation of strcpy_s for C++... I knew this would serve me one day... ^_^

http://msdn.microsoft.com/en-us/library/td1esda9.aspx

Edit: The solution is an extension standardized for C++0x

Fizzer did rightly comment this was not valid in the current C++ standard, and was quite true (as I could verify on g++ with -pedantic option checked).

Still, not only this is usable today on two major compilers (i.e. Visual C++ and g++), but this was considered for C++0x, as proposed in the following drafts:

The only change for C++0x being probably something like:

inline template <typename T, size_t size>
constexpr size_t array_size(T (&p)[size])
{
   //return sizeof(p)/sizeof(p[0]) ;
   return size ; // corrected after Konrad Rudolph's comment.
}

(note the constexpr keyword)

Edit 2

Johannes Schaub - litb's answer offers another, C++03 compliant way to do it. I'll copy paste the source here for reference, but do visit his answer for a complete example (and upmod it!):

template<typename T, size_t N> char (& array_size(T(&)[N]) )[N];

Which is used as:

int p[] = { 1, 2, 3, 4, 5, 6 };
int u[sizeof array_size(p)]; // we get the size (6) at compile time.

Many neurons in my brain fried to make me understand the nature of array_size (hint: it's a function returning a reference to an array of N chars).

:-)

Community
  • 1
  • 1
paercebal
  • 81,378
  • 38
  • 130
  • 159
  • http://www.csci.csusb.edu/dick/c++std/cd2/expr.html . See 5.19 Constant Expressions: 'functions ... shall not be used, and ... function-call ... operators shall not be used' – fizzer Oct 22 '08 at 21:27
  • Your link is about a "November 1996 Working Paper" draft: http://www.csci.csusb.edu/dick/c++std/cd2/ ... My own links are 2008 and are about C++0x – paercebal Oct 22 '08 at 21:32
  • I'm aware of that. The wording of the referenced paragraph is identical to the current standard. – fizzer Oct 22 '08 at 21:41
  • You're right. So I guess we all agree on the fact that today, the code above is an extention to C++ offered by g++ and Visual C++, and that the C++0x will most probably include something like that (i.e. using the constexpr keyword) for the next standard. – paercebal Oct 22 '08 at 21:52
  • No problem. Thanks for making the correction so prominently. – fizzer Oct 22 '08 at 21:55
  • @Fizzer: Thanks for your contribution. I learned I lot today, thanks to the discussion we shared. – paercebal Oct 22 '08 at 22:04
  • 1
    Er … why don't you simply return the `size` template argument in your function? The calculation of the size is completely redundant. – Konrad Rudolph Oct 23 '08 at 08:01
  • Good point. While this is not important (the calculation is done at compile time), I guess that Konrad is right and that the "size" parameter could be used directly. I'll verify this and correct the code. – paercebal Oct 23 '08 at 09:11
  • 1
    you can have a fully standard compliant constant expression by using this one. no need to wait for c++0x :) : template char (& array_size(T(&)[N]) )[N]; int dest2[sizeof array_size(src)]; – Johannes Schaub - litb Jan 04 '09 at 20:02
4

casting the C way (type) versus static_cast<type>(). see there and there on stackoverflow for the topic

Community
  • 1
  • 1
PW.
  • 3,727
  • 32
  • 35
  • You should make a summary, detailing with one sentence the exclusive use of each C++ cast, and finishing with something like "In C++, we can limit the cast's scope (const, static, etc.) and it's easier to find/grep in a large project". – paercebal Oct 22 '08 at 20:37
  • Right, but since you were the one who ask and that now I see that you are aware of those points on one hand. And, in the other hand, C++ cast are well covered by the ther two posts of which I provide links in my response, I think it's better to let the it concise. Anyhow thanks for the advice. – PW. Oct 23 '08 at 11:16
4

Local (automatic) variables declaration

(Not true since C99, as correctly pointed by Jonathan Leffler)

In C, you must declare all local variables at the start of the block in which they are defined.

In C++ it is possible (and preferable) to postpone variable definition before it must be used. Later is preferable for two main reasons:

  1. It increases program clarity (as you see the type of variable where it is used for the first time).
  2. It makes refactoring easier (as you have small cohesive chunks of code).
  3. It improves program efficiency (as variables are constructed just when they actually needed).
Alex Che
  • 6,659
  • 4
  • 44
  • 53
  • It also produces small, cohesive nuggets of code that make editing/refactoring easier. – Steve Fallows Oct 23 '08 at 19:10
  • It also frequently delays variable declaration until the variable can be intelligently initialized. This is a reduction in exposure, and if the variable is not of a basic type can lead to performance improvements. – David Thornley Oct 23 '08 at 19:18
  • I find this feature provides much cleaner code. This feature makes functional decomposition much easier. So much so that in C I will wrap my variables in { } to trap them to the minimum of lines possible. It's even worth using tiny {} block in C++. – mat_geek Dec 31 '08 at 02:55
  • 4
    This is bogus - C99 (nearly a decade old now) provides C++-style declaration of variables at any point in a function. The number of people who use it, though, is limited. – Jonathan Leffler Dec 31 '08 at 03:53
2

In response to Alex Che, and in fairness to C:

In C99, the current ISO standard spec for C, variables may be declared anywhere in a block, the same as in C++. The following code is valid C99:

int main(void)
{
   for(int i = 0; i < 10; i++)
      ...

   int r = 0;
   return r;
}
Community
  • 1
  • 1
chazomaticus
  • 15,476
  • 4
  • 30
  • 31
2

I'll offer something that is perhaps abundantly obvious, Namespaces.

c's crowded global scope:

void PrintToScreen(const char *pBuffer);
void PrintToFile(const char *pBuffer);
void PrintToSocket(const char *pBuffer);
void PrintPrettyToScreen(const char *pBuffer);

vs.

c++'s definable subdivisions of global scope, namespaces:

namespace Screen
{
   void Print(const char *pBuffer);
}

namespace File
{
   void Print(const char *pBuffer);
}

namespace Socket
{
   void Print(const char *pBuffer);
}

namespace PrettyScreen
{
   void Print(const char *pBuffer);
}

This is a bit of a contrived example, but the ability to classify tokens you define into scopes that make sense prevents confusing purpose of the function with the context in which it is called.

Community
  • 1
  • 1
Aaron
  • 3,726
  • 2
  • 26
  • 23
2

Following paercebal's construct using variable length arrays to get around the limitation that functions can't return constant expressions yet, here is a way to do just that, in a certain other way:

template<typename T, size_t N> char (& array_size(T(&)[N]) )[N];

I've written it in some of my other answers, but it doesn't fit anywhere better than into this thread. Now, well, here is how one could use it:

void pass(int *q) {
    int n1 = sizeof(q); // oops, size of the pointer!
    int n2 = sizeof array_size(q); // error! q is not an array!
}

int main() {
    int p[] = { 1, 2, 3, 4, 5, 6 };
    int u[sizeof array_size(p)]; // we get the size at compile time.

    pass(p);
}

Advantage over sizeof

  1. Fails for non-arrays. Will not silently work for pointers
  2. Will tell in the code that the array-size is taken.
Community
  • 1
  • 1
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I've been reading and testing your code for 10 minutes, and I'm still wondering what in the hell is array_size (I'm not that good at parsing C++ types, I guess). Anyway, +1 for the gizmo (and for the time I'll spend trying to learn/understand it) – paercebal Oct 07 '10 at 20:28
  • Ok, I got it. It's the prototype of a function returning the reference to an array of N chars. The sizeof operator is used to get the size of the returned array of chars. I did not know the sizeof operator could be used on function prototypes to return the size of its returned type. Thanks! – paercebal Oct 07 '10 at 20:38
1

std::copy vs. memcpy

First there are usability concerns:

  • memcpy takes void pointers. This throws out type safety.
  • std::copy allows overlapping ranges in certain cases (with std::copy_backward existing for other overlapping cases), while memcpy does not ever allow it.
  • memcpy only works on pointers, while std::copy works on iterators (of which pointers are a special case, so std::copy works on pointers, too). This means that you can, for example, std::copy elements in a std::list.

Surely all of this extra safety and generality comes at a price, right?

When I measured, I found that std::copy had a slight performance advantage over memcpy.

In other words, it seems as if there is no reason to use memcpy in real C++ code.

Community
  • 1
  • 1
David Stone
  • 26,872
  • 14
  • 68
  • 84
0

Overloaded functions:

C:

AddUserName(int userid, NameInfo nameinfo);
AddUserAge(int userid, int iAge);
AddUserAddress(int userid, AddressInfo addressinfo);

C++ equivalent/replacement:

User::AddInfo(NameInfo nameinfo);
User::AddInfo(int iAge);
User::AddInfo(AddressInfo addressInfo);

Why it's an improvement:

Allows the programmer to express the interface such that the concept of the function is expressed in the name, and the parameter type is only expressed in the parameter itself. Allows the caller to interact with the class in a manner closer to an expression of the concepts. Also generally results in more concise, compact and readable source code.

Steve
  • 1,065
  • 15
  • 34
  • While the pass-by-copy would probably be better as pass-by-const-reference, I've seen enough of this pattern to believe the non-use of overloaded function in stead really was a "I'm not comfortable with C++ overloaded functions" statement. +1. (Who the hell downmodded you anyway?) – paercebal Oct 07 '10 at 20:44
  • 1
    @parcebal: I did, for one. These functions should be named differently. What's the "C++ equivalent/replacement" for `AddUserGrade(int userid, int grade)`? – Ben Voigt Jul 03 '11 at 00:38
  • I've found that when dealing with anything but the simplest and smallest cases the idea to have different functions with the same name is stupid and makes the code harder to follow for no real gain. – 6502 May 22 '12 at 06:45
0

iostreams

Formatted I/O may be faster using the C runtime. But I don't believe that low-level I/O (read,write,etc.) is any slower with streams. The ability to read or write to a stream without caring if the other end is a file, string, socket or some user-defined object is incredibly useful.

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
0

In c, much of your dynamic functionality is achieved by passing about function pointers. C++ allows you to have Function Objects, providing greater flexibility and safety. I'll present an example adapted from Stephen Dewhurst's excellent C++ Common Knowledge

C Function Pointers:

int fibonacci() {
  static int a0 = 0, a1 =1; // problematic....
  int temp = a0;
  a0 = a1;
  a1 = temp + a0;
  return temp;
}

void Graph( (int)(*func)(void) );
void Graph2( (int)(*func1)(void), (int)(*func2)(void) ); 

Graph(fibonacci);
Graph2(fibonacci,fibonacci);

You can see that, given the static variables in the function fibonacci(), the order of execution of Graph and Graph2() will change the behavior, not withstanding the fact that call to Graph2() may have unexpected results as each call to func1 and func2 will yield the next value in the series, not the next value in an individual instance of the series with respect to the function being called. (Obviously you could externalize the state of the function, but that would be missing the point, not to mention confusing to the user and complicating to the client functions)

C++ Function Objects:

class Fib {
  public:
    Fib() : a0_(1), a1_(1) {}
    int operator();
  private:
    int a0_, a1_;
};
int Fib::operator() {
    int temp = a0_;
    a0_ = a1_;
    a1_ = temp + a0_;
    return temp;
}


template <class FuncT>
void Graph( FuncT &func );

template <class FuncT>
void Graph2( FuncT &func1, FuncT &func2); 

Fib a,b,c;
Graph(a);
Graph2(b,c);

Here, the order of execution of the Graph() and Graph2() functions does not change the result of the call. Also, in the call to Graph2() b and c maintain separate state as they are used; each will generate the complete Fibonacci sequence individually.

Aaron
  • 3,726
  • 2
  • 26
  • 23
0

new in C++ vs malloc in C. (for memory management)

new operator allows class constructors to be called whereas malloc does not.

kal
  • 28,545
  • 49
  • 129
  • 149
-4

Nearly any use of void*.

user23167
  • 487
  • 5
  • 12
  • You should offer an example of C code at its maximum safety using void *, and the example of C++ code using whatever solution to offer the same solution. This would upmod your post. – paercebal Oct 22 '08 at 21:54