tags:

views:

584

answers:

10

There are a few things that I almost always do when I put a class together in C++.

1) Virtual Destructor 2) Copy constructor and assignment operator (I either implement them in terms of a private function called Copy(), or declare them private and thus explicitly disallow the compiler to auto generate them).

What things do you find are almost always useful?

+2  A: 
Federico Ramponi
I can understand you'd often use the second one, but the first one? In most cases I'd say a to_string() method would seem like a more obvious choice.
Leon Timmermans
+3  A: 

Adding a semicolon after the class definition. This continuously bites me in the ass every time I forget to do it since gcc's error messages are vague and it generally says something like "can't define a type in the return type of a function" which doesn't mean a whole lot...

Greg Rogers
+9  A: 

I find turning on the gcc flags -Wall, -Werror, and (this is the fun one) -Weffc++ help catch a lot of potential problems. From the gcc man page:

  -Weffc++ (C++ only)
      Warn about violations of the following style guidelines from Scott
      Meyers’ Effective C++ book:

      ·   Item 11:  Define a copy constructor and an assignment operator
          for classes with dynamically allocated memory.

      ·   Item 12:  Prefer initialization to assignment in constructors.

      ·   Item 14:  Make destructors virtual in base classes.

      ·   Item 15:  Have "operator=" return a reference to *this.

      ·   Item 23:  Don’t try to return a reference when you must return
          an object.

      and about violations of the following style guidelines from Scott
      Meyers’ More Effective C++ book:

      ·   Item 6:  Distinguish between prefix and postfix forms of incre-
          ment and decrement operators.

      ·   Item 7:  Never overload "&&", "││", or ",".

      If you use this option, you should be aware that the standard
      library headers do not obey all of these guidelines; you can use
      grep -v to filter out those warnings.
Greg Hewgill
+1  A: 

In header files, do

#ifndef __SOMEDEFINE__
#define __SOMEDEFINE__

#endif

In VS, I add

#pragma warning(disable: 4786)

Oh, I also use

#include <inttypes.h>

cuz I <3 C99 types.

Alan
I posted a question about header guards yesterday and apparently a double-underscore is a no-no. See here:http://stackoverflow.com/questions/314983/include-header-guard-format
Rob
Warning c4786 doesn't exist anymore (it's not in the MSDN). That's the one about decorated name of templates being too long for the SBR file, right? I think they fixed that in VC6 or VS2002.
James Curran
It wasn't fixed in VC6, and IIRC not in VS2002 either. @Rob, yep you're right, I shouldn't have used the double underscore.
Alan
+1  A: 

I usually include an enum of return codes, so the class can tell its callers what happened in its member functions. Most often, this will be the only type returned by all the members of the class. All results are passed back by reference.

Marcin
+4  A: 

The first i do when putting a class together is putting some doxygen comment above it about why it exists and what it does.

I once worked on a group project where they said they want to document the stuff at the end of the project. And it was all of a mess to put the comments into the code later on. I don't want to have this happen again.

Johannes Schaub - litb
+13  A: 

Oddly, most of the suggestions here are things I specifically don't do.

  • I don't make dtors virtual unless I am designing it specifically to be inherited. It adds a lot of overhead and prevents automatic inlining, which is bad since most dtors are empty anyways (and few classes benefit from inheritance)
  • I don't make copy ctor/assignment op unless the defaults won't work -- and if it won't, I may want to reconsider the design. Remember, between string & vector, there's hardly ever a reason to call new anymore. And creating your own copy ctor identical to the default one will almost certainly be less efficient.
  • I don't add string cast. It causes too many problems where the cast is called silently where you didn't intend it to be. Better to add a ToString() method.
  • I don't add a friend oper<<, because friends are evil and messy. Better to add a Display(ostream) method. Then the oper<< can call that, and doesn't need to be a friend. In fact, you could make the oper<< a template function calling Display() and never have to worry about it again.
James Curran
I can't agree more.
sep
+1 from me too. Regarding the copy-ctor and oper=, boost::noncopyable can be used defensively, before implementing the actual copy semantics.
efotinis
+1 for no string cast, -1 for not being a friend of ostream operator<<
Marcin
@Marcin, why -1? I still have a ostream oper<<; I just do it without it being a friend.
James Curran
I hate using friend declarations except in very rare circumstances. It shouldn't be a part of your standard design to always use it for operator<<. Great idea with the template for operator<< James.
Tom Leys
I tend to agree with Scott Meyers on this, operator<< (and >>) are OK to befriend.
Greg Rogers
+1  A: 

I start by calling the development environment macro that sets up the include guards (#ifdefs and/or #pragma once).

Next I stub out the class name and any namespace it will be in, without adding any functionality at all.

Then I create the unit test file for that class, and add the first test (usually a parameter constructor test).

I just work from there, writing and refactoring the class as I think about what I really need from it. Things I tend to test specifically: const-correctness, implicit and explicit conversions, types of exceptions thrown, etc.

MadKeithV
+3  A: 

Stop and think

Patrick
+1  A: 

The first thing I do with a totally new class file is to write several paragraphs of comments on what the class does, why it exists, what classes it uses and how to use it. It should be enough that someone who randomly opens up a file in a module knows enough from that comment to find the file they are actually looking for.

I agree with James - I am very careful not to add functionality to a class that does not need it, most classes do not need a virtual destructor (or a destructor at all). If they do, I question why they don't just use smart pointers and other automatic memory management. Obviously there are many classes (i.e smart scoped locks) that DO need a destructor but it is not just a matter of course to make on.

Tom Leys