More OOP Concerns
If you design more than one class in a hierarchy, be sure that every entity is in its proper place. In general, push items as far up the hierarchy as they can logically go. Suppose that you have Dog inheriting from Mammal, which inherits from Animal. A bark() method can logically be associated with the Dog class, but a method like give_birth() should be placed in Mammal, and a method eat() should be placed in Animal. In this way we fulfill the purpose of the hierarchy, which is to generalize as much as possible.
Don't be afraid of class-level (static) entities. They can be useful, among other things, for tracking information relating to the instances created. Ask yourself whether there are data or operations that properly belong to the class itself rather than any particular instance.
When you write constructors for your class, give them variety and flexibility as appropriate. If it makes sense to take either an integer or a string as a parameter, write both versions. Remember that the constructors must have distinguishable method signatures.
Also remember that constructors will sometimes be silently called as part of a type conversion. This can often lead to errors. The keyword explicit modifies a constructor declaration so that the constructor will never be called implicitly; use it as appropriate.
Form the habit of using initializer lists for your constructors wherever you can, which can improve the readability of code. An important feature of the initializer is that it guarantees that the values are set before the constructor begins execution. For this reason, they are also the only way to initialize const values and reference data members and embedded/base subobjects.
Consider whether you need a custom copy constructor. If you have dynamically allocated data, you will need one. Do you want assignment to do a deep copy or a shallow copy?
When you design the methods of the class, pay careful attention to the naming. This is almost more of an art than a science. A method name should be descriptive of its task and purpose, and it should offer a clue as to whether it changes its receiver or merely accesses it (the getter/setter convention I use clearly is an exception to this). A name such as add or delete implies that something is being changed; a name such as find or retrieve does not. If the effect of a method seems inconsistent with its name, either rename it or split it into two methods with appropriate names.
Also make sure that a method with no "side effects" is marked const (and the rest are unmarked). This is the only way the compiler can tell whether a method call on a const object is allowed. If the method is defined outside the class body, both the prototype and the definition must agree.
The new keyword mutable will allow certain class members to be changed, even on a const object. This is to allow implementation details such as caching or any other operation that changes the object's internal state without changing the state that it exposes to the world.