While both may be described as C++ classes, there is a big difference between, say, a Complex number and, say, a BankAccount. For example, there is a large, well-established mathematical theory of complex numbers, but essentially none for bank accounts. One consequence is that it is simply much easier to develop a Complex class containing features that one may be reasonably certain will makes sense across a wide range of applications. This is much less true of any BankAccount class one could construct.
A more important distinction underlies the resulting design differences. The ``theory'' of complex numbers revolves around the properties of complex values, not objects. Mathematical approaches typically abstract over the actual identities of objects possessing (Re, Im) attributes, and just deal with the values themselves -- the complex quantity (2.4, 17.17) remains the same regardless of which or how many objects report this quantity as real(), and imag() attribute functions. By design, many operations don't care about the objects, and just deal with the quantities. However, this would be a losing attitude for a class like BankAccount. For example, even when we happen to both have the same bank balance, the fact that a particular identifiable BankAccount instance belongs to you and not me is an obvious but critical design issue. These differences result in different styles, approaches, and plans of attack for designing the associated classes and utilities. For example, while it is perfectly sensible to write a ``constructive'' function that accepts two complex numbers and returns a third representing their sum, there is hardly ever a reason to create a function that accepts two bank accounts and returns a third representing (among other things) the sum of their balances. Instead, the BankAccount class contains methods such as withdraw, transfer, and so on that mutate the states of particular objects.
Libg++ contains substantially more components like Complex than those like BankAccount. Many classes maintain ``value semantics'', in a manner more similar to classic ADT approaches than to classic OO approaches. There are some distinct advantages to ADT approaches in those cases where they are appropriate:
However, no one ever uses a straight ADT approach in designing classes. For example, a ``purist'' approach to a Stack ADT would define push to return a new stack state value, different than the original. So, if Stack s denoted a stack with a million items on it, s.push(23) would return something representing a million and one. Nobody wants this, partially just because of efficiency. Even with a lot of underlying cleverness, too much data copying is required to represent new values resulting from pushes. However, it also represents the point at which object-oriented thinking rightfully creeps into ADT-based design. When clients push 17 onto Stack s, they essentially always wants s itself to change, not to construct a new, distinct representation. For this reason, OO approaches to ADTs usually include mutative operations on objects that propel them into different states rather than, or in addition to, operations that construct new representations of new states and values. (Non-OO ADT approaches often do this too, but usually describe them differently. For example, they might talk about the ``symbol s being rebound to a new value'' after a push.)
The twist in libg++ and other OO libraries is to support some mixture of value-oriented and object-oriented usage, almost always within the very same classes. This is a natural practice, especially in C++, since the C base of C++ already does this. For example, unlike most procedural languages, C contains both the constructive value-oriented + operation for adding built-in number types, as well as the (vaguely) object-oriented += ``method'' for mutating number objects.
Libg++ was originally a set of experiments in how to go about meeting the occasionally conflicting demands of the two approaches. The two approaches do indeed sometimes conflict, and lead to different trade-offs seen in different classes. Many of these, in turn, reflect trade-offs made in the language itself. Stroustrup has described C++ as a language supporting both data abstraction and OO design. Crosses between ADT and OO designs often find themselves at the very borders of both kinds of support, in ways that most other C++ designs do not.