(Click here, next to the previous article)
An alternative solution is to let squarematrixbase store a pointer to the memory area of the matrix value. And once it stores this pointer, it can also store the matrix size. The final design is roughly like this:
Template <typename T>
Class squarematrixbase {
Protected:
Squarematrixbase (STD: size_t N, T * pmem)// Store matrix size and
: Size (N), pdata (pmem ){}// PTR to Matrix Values
Void setdataptr (T * PTR) {pdata = PTR ;}// Reassign pdata
...
PRIVATE:
STD: size_t size;// Size of Matrix
T * pdata;// Pointer to Matrix Values
};
In this way, let the derived classes (derived class) decide how to allocate memory. Some implementations may decide to store matrix data directly in squarematrix object:
Template <typename T, STD: size_t n>
Class squarematrix: Private squarematrixbase <t> {
Public:
Squarematrix ()// Send matrix size and
: Squarematrixbase <t> (n, data ){}// Data PTR to base class
...
PRIVATE:
T data [N * n];
};
This type of objects does not require dynamic memory allocation (dynamic memory allocation), but these objects may be very large. An optional solution is to place the data of each matrix on the heap (HEAP:
Template <typename T, STD: size_t n>
Class squarematrix: Private squarematrixbase <t> {
Public:
Squarematrix ()// Set base class data PTR to null,
: Squarematrixbase <t> (n, 0 ),// Allocate memory for Matrix
Pdata (New T [N * n])// Values, save a PTR to
{This-> setdataptr (pdata. Get ());}// Memory, and give a copy of it
...// To the base class
PRIVATE:
Boost: scoped_array <t> pdata;// See item 13 for info on
}; // Boost: scoped_array
Regardless of where the data is stored, the key result from the expanded perspective is: Many of squarematrix's -- maybe all -- member functions (member functions) you can simply call the base class versions (base class version) of inline, which is shared with all other matrices that hold the same data type, regardless of their size. At the same time, squarematrix objects of different sizes are of different types, so, for example, even if squarematrix <double, 5> and squarematrix <double, 10> objects uses the same member functions (member functions) in squarematrixbase <double> and does not have the chance to transmit a squarematrix <double, 5> object to a squarematrix <double, 10>. Good, isn't it?
Good, yes, but not free. The version of the invert in which the matrix size is hard and fixed is likely to generate better code than passing the size as a function parameter or a shared version stored in the object. For example, in a version with a specific size, sizes (size) will become compile-time constants (compile-time constant), so it is suitable for optimization like constant propagation, these include embedding them into the generated command as immediate operands (immediate operand. This is not possible in size-independent version.
On the other hand, the unique invert version is used for multiple matrix sizes to reduce the executable code size, and the working set (workspace) Size of the program can also be reduced and the Instruction Cache can be improved) locality of reference in ). These can make the program run faster and overpay any optimization of the lost size-specific versions (specific size version) for invert. Which one is more cost-effective? The only resolution method is to test two methods on your specific platform and typical datasets and observe their behavior.
Another efficiency concerns the size of objects. If you are not careful, moving the size-independent version of the function (size-independent version) to a base class will increase the overall size of each object. For example, in the code I just showed, even if every derived class (derived class) has a way to get data, each squarematrix object has a pointer to its data stored in squarematrixbase class, which increases the size of each squarematrix object by at least one pointer. It is possible to change the design so that these pointers are no longer necessary, but this is another transaction. For example, a base class stores a protected pointer pointing to matrix data, reducing the encapsulation described in item 22. It may also complicate Resource Management: if the base class stores a pointer to the matrix data, however, the data can be dynamically allocated or physically stored in the derived class Object (derived class Object) (as we can see ), how does it determine whether the pointer should be deleted? There are answers to these questions, but the more you want to make them more sophisticated, the more complicated it will become. Under some conditions, a small amount of code repetition is like a relief.
This item only discusses the expansion caused by non-type template parameters (non-type template parameter), but type parameters (type parameter) can also lead to expansion. For example, in many platforms, int and long have the same binary representation. Therefore, we can say that the member functions (member function) of vector <int> and vector <long>) it is likely to be the same-the just-Right Explanation of expansion. Some connection programs merge the same function implementation, and some do not, which means that some templates in some environments are instantiated on int and long, which can lead to code duplication. Similarly, on most platforms, all pointer types have the same binary representation, so they hold pointer-type templates (for example, list <int *>, list <const int *>, list <squarematrix <long, 3> *>) should generally be able to use a single underlying implementation of each member function (member function. In typical cases, this means the member functions (member function) that works with stronugly typed pointers (strong type pointer) (that is, T * pointer) you can call the functions that work with the untyped pointers (no type pointer) (that is, the void * pointer. Some Standard C ++ libraries are implemented for templates such as vector, deque, and list. If you care about the code expansion caused by your template, you may need to develop the template using the same method.
Things to remember
- Templates (Template) generates multiple classes and multiple functions, so some template code that does not depend on the template parameter (Template parameter) will cause expansion.
- The expansion caused by non-type template parameters (non-type template parameters) can often be replaced by the function parameters (function parameter) or class data members (class data member) template parameters (Template parameter) and eliminate.
- The expansion caused by type parameters can be reduced by sharing the instantiation types with the same binary representation.