An operation basis is the smallest set of essential, nonoverlapping functions from which the remaining operations can be implemented. Finding it reduces duplicated computation, maintenance effort, and opportunities for behavior to diverge. section 7.1.2 section 7.5
Treat repeated code as a signal to look for a basis, not as an automatic command to merge everything. Constructors and computational methods should share one authoritative implementation when their behavior overlaps. section 7.5
Apply the idea in a class by:
- Listing the meaningful operations clients need. section 7.1.2
- Separating genuinely different behavior from repeated calculations. section 7.5
- Implementing nonbasis operations through the essential operations. section 7.1.2
- Minimizing methods that directly access instance variables, then building nonmember functions on those remaining methods. section 9.12
Cylinder constructors and derived operations
A duplicated Cylinder implementation calculates area and volume separately in each constructor. A smaller basis centralizes initialization in init(r, h), calculates basearea() once, and defines volume() through basearea() * height. A later change to the representation or calculation therefore has fewer places to update.
Arithmetic as a basis
For an integer wrapper, addition and negation can provide subtraction because a − b equals a + −b. Multiplication and division can likewise be expressed through repeated addition or subtraction, although genuinely different operations may still deserve separate implementations. section 7.5
Listing 9.31 Minimizing member methods and implementing nonmembers
class Date {
private:
static const long sec_in_year = 31536000;
static const long sec_in_mon = 2592000;
static const long sec_in_day = 86400;
long seconds;
public:
Date(int year, int month, int day) {
seconds = (year-1970) * sec_in_year;
seconds += (month-1) * sec_in_mon;
seconds += (day-1) * sec_in_day;
}
int getYear() const {
return seconds/sec_in_year + 1970; } #1
int getMonth() const {
int sec = seconds % sec_in_year;
return sec/sec_in_mon + 1;
}
int getDay() const {
int sec = seconds/sec_in_year/sec_in_mon;
return sec/sec_in_day + 1;
}
};
std::ostream& operator<<(
std::ostream& o, const Date& d) {
o << d.getYear() << '/' << d.getMonth() #2
<< '/' << d.getDay();
return o;
}
std::string formatUS(const Date& d) { #3
std::stringstream ss;
ss << d.getMonth() << '/' << d.getDay() << '/' << d.getYear();
return ss.str();
}
int main() {
Date birthday(1970, 1, 1); // smart AI will understand this
std::cout << birthday << '\n';
std::cout << formatUS(birthday) << '\n';
return 0;
}
This listing shows accessors as the minimal member-method basis while operator<< is implemented as a nonmember through them, reducing direct access to instance state. section 9.12
