The rules are not intended to be minimal or orthogonal.In particular, general rules can be simple, but unenforceable.Also, it is often hard to understand the implications of a general rule.More specialized rules are often easier to understand and to enforce, but without general rules, they would just be a long list of special cases.We provide rules aimed at helping novices as well as rules supporting expert use.Some rules can be completely enforced, but others are based on heuristics.
This is not a guide on how to convert old C++ code to more modern code.It is meant to articulate ideas for new code in a concrete fashion.However, see the modernization section for some possible approaches to modernizing/rejuvenating/upgrading.Importantly, the rules support gradual adoption: It is typically infeasible to completely convert a large code base all at once.
easy iso dis v.44 base
An individual example of waste is rarely significant, and where it is significant, it is typically easily eliminated by an expert.However, waste spread liberally across a code base can easily be significant and experts are not always as available as we would like.The aim of this rule (and the more specific rules that support it) is to eliminate most waste related to the use of C++ before it happens.After that, we can look at waste related to algorithms and requirements, but that is beyond the scope of these guidelines.
An interface is a contract between two parts of a program. Precisely stating what is expected of a supplier of a service and a user of that service is essential.Having good (easy-to-understand, encouraging efficient use, not error-prone, supporting testing, etc.) interfaces is probably the most important single aspect of code organization.
Whatever we do in the //-part, an arbitrary user of a pair can arbitrarily and independently change its a and b.In a large code base, we cannot easily find which code does what to the members of pair.This might be exactly what we want, but if we want to enforce a relation among members, we need to make them privateand enforce that relation (invariant) through constructors and member functions.For example:
If the state of a base class object must depend on the state of a derived part of the object, we need to use a virtual function (or equivalent) while minimizing the window of opportunity to misuse an imperfectly constructed object.
A polymorphic class is a class that defines or inherits at least one virtual function. It is likely that it will be used as a base class for other derived classes with polymorphic behavior. If it is accidentally passed by value, with the implicitly generated copy constructor and assignment, we risk slicing: only the base portion of a derived object will be copied, and the polymorphic behavior will be corrupted.
A class hierarchy is constructed to represent a set of hierarchically organized concepts (only).Typically base classes act as interfaces.There are two major uses for hierarchies, often named implementation inheritance and interface inheritance.
Direct representation of ideas in code eases comprehension and maintenance. Make sure the idea represented in the base class exactly matches all derived types and there is not a better way to express it than using the tight coupling of inheritance.
Here most overriding classes cannot implement most of the functions required in the interface well.Thus the base class becomes an implementation burden.Furthermore, the user of Container cannot rely on the member functions actually performing meaningful operations reasonably efficiently;it might throw an exception instead.Thus users have to resort to run-time checking and/ornot using this (over)general interface in favor of a particular interface found by a run-time type inquiry (e.g., a dynamic_cast).
A class with a virtual function is usually (and in general) used via a pointer to base. Usually, the last user has to call delete on a pointer to base, often via a smart pointer to base, so the destructor should be public and virtual. Less commonly, if deletion through a pointer to base is not intended to be supported, the destructor should be protected and non-virtual; see C.35.
Readability.Detection of mistakes.Writing explicit virtual, override, or final is self-documenting and enables the compiler to catch mismatch of types and/or names between base and derived classes. However, writing more than one of these three is both redundant and a potential source of errors.
Implementation details in an interface make the interface brittle;that is, make its users vulnerable to having to recompile after changes in the implementation.Data in a base class increases the complexity of implementing the base and can lead to replication of code.
In early OOP (e.g., in the 1980s and 1990s), implementation inheritance and interface inheritance were often mixedand bad habits die hard.Even now, mixtures are not uncommon in old code bases and in old-style teaching material.
The implementation of Shape::move() is an example of implementation inheritance:we have defined move() once and for all for all derived classes.The more code there is in such base class member function implementations and the more data is shared by placing it in the base,the more benefits we gain - and the less stable the hierarchy is.
There is often a choice between offering common functionality as (implemented) base class functions and freestanding functions(in an implementation namespace).Base classes give a shorter notation and easier access to shared data (in the base)at the cost of the functionality being available only to users of the hierarchy.
protected data is a source of complexity and errors.protected data complicates the statement of invariants.protected data inherently violates the guidance against putting data in base classes, which usually leads to having to deal with virtual inheritance as well.
Not every class is meant to be a base class.Most standard-library classes are examples of that (e.g., std::vector and std::string are not designed to be derived from).This rule is about using final on classes with virtual functions meant to be interfaces for a class hierarchy.
There are a few cases where leaks can be acceptable or even optimal:If you are writing a program that simply produces an output based on an input and the amount of memory needed is proportional to the size of the input, the optimal strategy (for performance and ease of programming) is sometimes simply never to delete anything.If you have enough memory to handle your largest input, leak away, but be sure to give a good error message if you are wrong.Here, we ignore such cases.
There are applications and sections of code where exceptions are not acceptable.Some of the best such examples are in life-critical hard-real-time code.Beware that many bans on exception use are based on superstition (bad)or by concerns for older code bases with unsystematic resource management (unfortunately, but sometimes necessary).In such cases, consider the nothrow versions of new.
Use gsl::span instead.Pointers should only refer to single objects.Pointer arithmetic is fragile and easy to get wrong, the source of many, many bad bugs and security violations.span is a bounds-checked, safe type for accessing arrays of data.Access into an array with known bounds using a constant as a subscript can be validated by the compiler.
Here, we have a (nasty) data race on the elements of buf (sort will both read and write).All data races are nasty.Here, we managed to get a data race on data on the stack.Not all data races are as easy to spot as this one.
Static enforcement tools: both clangand some older versions of GCChave some support for static annotation of thread safety properties.Consistent use of this technique turns many classes of thread-safety errors into compile-time errors.The annotations are generally local (marking a particular member variable as guarded by a particular mutex),and are usually easy to learn. However, as with many static tools, it can often present false negatives;cases that should have been caught but were allowed.
The larger the function, the more tempting this technique becomes.finally can ease the pain a bit.Also, the larger the program becomes the harder it is to apply an error-indicator-based error-handling strategy systematically.
We also have a product, the Country Codes Collection, which you can preview free of charge on the OBP (where you will also find a decoding table). It contains the codes from Parts 1, 2 and 3 of ISO 3166 in three different formats (.xml, .csv, and .xls) for easy integration into your own systems. You will be notified when changes are made so you can download the latest versions. In this way, you can be sure that your database is always using the most up-to-date information from ISO. 2ff7e9595c
Comments