18 Make interfaces easy to use correctly and hard to use incorrectly

内容分享1小时前发布
0 0 0

There are many kinds interfaces in C++ such as function interfaces, class interfaces and template interfaces.However, some clients may use one interface incorrectly, and the designer of this interface is at least partially to blame. Ideally, if an attempted use of an interface won t do what the client expects, the code won t compile; and if the code does compile , it will do what the client wants.

Developing interfaces that are easy to use correctly and hard to use incorrectly requires that you consider the kinds of mistakes that clients might make. For example, suppose you are designing the constructor for a class representing dates in time:

class Date{
public:
  Date(int month, int day, int year);
};

As the designer for the interface, we may not use it incorrectly.However, for the clients , it is easy for them to make mistakes such as :

Date(30, 3, 2014); // month and day in the wrong order

Date(2, 30, 2014); // should not be "2"

Indeed, the type system is the primary ally in preventing undesirable code from compiling. In this case, we can introduce simple wrapper types to distinguish days, months , years, then use these types in the Date constructor :

struct Day{
explicit Day(int d) : val(d){}
int val;
};

struct Month{
explicit Month(int m) : month(m){}
int month;
}:

struct Year{
explicit Year(int y) : year(y){}
int year;
};

class Date{
public:
  Date(const Month& m, const Day& d, const Year& y);
....
};


Date d(3, 30, 2014); // wrong !
Date d(Day(30), Month(3), Year(2014)); // wrong !
Date d(Month(3), Day(30), Month(2014)); //ok!

What is more, it is better to make Day,Month and Year full-fledged classes with encapsulated data.

class Day{...};
class Month{..};
class Year{...};

Once the right types are in place, it would be reasonable to restrict the values of those types. For example, there are only 12 valid month values, so the Month type should reflect that. Let us modify the Month:

class Month{
public:
  static Month Jan(){return Month(1);}
  static Month Feb(){return Month(2);}
...
  static Month Dec(){return Month(12);}
...
 private:
  explicit Month(int m); //prevent creation of new Month values
};

Date d(Month::Jan(), Day(30), Year(2014));

What s more, we can also restrict what can be done with a type to prevent likely client errors such as const - qualifying the return type.

if(a*b = c) ...

And also, when it comes to manage the resources, we have introduced the shared_ptr to do this. And the shared_ptr can also allow a resource-release function – “a deleter ” to be bound to the smart point when the smart pointer is created.In this case, shared_ptr makes it possible for an interface designer to prevent a host of other client errors regarding resource release.

An especially nice feature of tr1::shared_ptr is that it automatically uses its per-pointer deleter to eliminate another potential client error, the “cross-DLL problem”. This problem crops up when an object is created using new in one dynamically linked library(DLL) but is delete in a different DLL. On many platforms, such cross-DLL new/delete pairs lead to runtime errors.tr1::shared_ptr avoids the problem, because its default deleter uses delete from the same DLL where the tr1::shared_ptr is created. This means, for example, that if Stock is a class derived from Investment and createInvestment is implemented like this,

std::tr1::shared_ptr<Investment> createInvestment()
{
 return std::tr1::shared_ptr<Investment>(new Stock);
}

the return tr1::shared_ptr can be passed among DLLs without concern for the corss-DLL problem. The tr1::shared_ptr s pointing to the Stock keep track of which DLL s delete should be used when the reference count for the Stock becomes zero.

Conclusion

  • Good interfaces are easy to use correctly and hard to use incorrectly. You should strive for these characteristics in all your interfaces.
  • Ways to facilitate correct use include consistency in interfaces and behavioral compatibility with built-in types.
  • Ways to prevent errors include creating new types, restricting operations on types, constraining object values, and eliminating client resource management responsibilities.
  • tr1::shared_ptr supports custom deleters.This prevents the cross-DLL problem , can be used to automatically unlock mutexes.
© 版权声明

相关文章

暂无评论

none
暂无评论...