A good API will not only provide easy to use interfaces, but also provide hard to mis-used interfaces. Usually, the later point is the fundamental of the earlier one. Consider you want to write a date class, there are thousands of ways to write it. Let’s see two simple examples:
1 2 3 4 5 6 7 8 9 10 11 12
// A date class which is easy to use but also easy to use wrong. classDate { public: Date(int month, int day, int year); ... };
// Both are ok, but some european programmer may use it wrong. // Because european time is dd/mm/yyyy instead of mm/dd/yyyy. Date d(3, 4, 2000); Date d(4, 3, 2000);
// Class date which is easy to use and not easy to use wrong. classDate { public: structDay { explicitDay(int d):val(d){} int val; }; structMonth { explicitMonth(int m):val(m){} int val; }; structYear { explicitYear(int y):val(y){} int val; };
private: Month m_month; Day m_day; Year m_year; };
Date d1(Date::Month(3), Date::Day(3), Date::Year(2000)); // Ok
// Compilation error! // no matching function for call to 'Date::Date(Date::Day, Date::Month, Date::Year)' Date d2(Date::Day(3), Date::Month(3), Date::Year(2000));
Let’s see another example, suppose your interface returns a dynamically allocated resource which need to be released after all. The chances are programmers might forget to release it causing leaks. Returning a smart pointer maybe a good idea.
1 2
// Not easy to use interface, client has to remember to release the resource Investment* createInvestment();
1 2 3 4 5 6 7 8 9 10 11
// Better interface returning smart pointer. Resource is released automatically // Here releaseInvestment is deleter of shared_ptr to release resources sdt::tr1::shared_ptr<Investment> createInvestment() { new Investment iv; std::tr1::shared_ptr<Investment> retVal(iv, releaseInvestment);