Virtual functions in ctor and dtor

During construction and destruction of an object you are able to call virtual functions. But you should not do this. That’s because it is a source for errors as the behavior of the application may be differ from your expectations or the expectations of other developers which have to change your code in future.

Before we start to think about the ctor and dtor issue we want to have a look at the normal behavior of a virtual function. The following source code shows a base class with a virtual function which creates some logging output and another function which executes something and writes a log entry. A derived class will use the base class implementations but overwrites the virtual function for the log entry. So we can create an object of the derived class, executed the functions provided by the base class and the log information of the derived class is used as we have overwritten the virtual function.

class BaseClass
{
public:
  void DoSomething()
  {
    //do something...

    //... and log what you habe done
    LogClassInformation();
  }

  virtual void LogClassInformation()
  {
    std::cout << "BaseClass log" << std::endl;
  }
};

class DerivedClass : public BaseClass
{
public:
  void LogClassInformation()
  {
    std::cout << "DerivedClass log" << std::endl;
  }
};


int _tmain(int argc, _TCHAR* argv[])
{
  DerivedClass myClass = DerivedClass();

  myClass.DoSomething();

  return 0;
}

 

But why is it dangerous to make a virtual function call in ctor and dtor? Let us think about the execution order of ctor and dtor to understand the issue. Again we have a base class and a derived class. If we create an object of the derived class, the ctor of the base class is called first followed by the ctor of the derived class. If you base class ctor will call a virtual function which is overwritten by the derived class we now have the issue that we want to execute some functionality of a class which is not initialized by a ctor yet. So the class members may contain not initialized members and the function execution can result in an undefined behavior. As this is dangerous, C++ gives you no way to call the derived class function. That is such a fundamental language behavior that during base class construction the type of the object is that of the base class itself and not the derived class.

The same is true for the dtor. During destruction of the class the dtor of the derived class was already called when the dtor of the base class is executed. It is not safe to call a function of the derived class as the class members are no longer valid. Therefore C++ changes the object type during the dtor call to the base class object on entry of the base class dtor.

The following source code shows an according example.

class BaseClass
{
public:
  BaseClass()
  {
    std::cout << "BaseClass ctor" << std::endl;
    LogClassInformation();
  }

  ~BaseClass()
  {
    std::cout << "BaseClass dtor" << std::endl;
    LogClassInformation();
  }

  virtual void LogClassInformation()
  {
    std::cout << "BaseClass log" << std::endl;
  }
};

class DerivedClass : public BaseClass
{
public:
  DerivedClass()
  {
    std::cout << "DerivedClass ctor" << std::endl;
    LogClassInformation();
  }

  ~DerivedClass()
  {    
    std::cout << "DerivedClass dtor" << std::endl;    
    LogClassInformation();
  }

  void LogClassInformation()
  {
    std::cout << "DerivedClass log" << std::endl;
  }
};


int _tmain(int argc, _TCHAR* argv[])
{
  DerivedClass myClass = DerivedClass();
  
  return 0;
}

The console application will create the following output.

BaseClass ctor

BaseClass log

DerivedClass ctor

DerivedClass log

DerivedClass dtor

DerivedClass log

BaseClass dtor

BaseClass log

 

As you can see the base class ctor and dtor will call the base class logging function and not the one of the derived class. After reading this article you may argue that this is the expected behavior and therefore it is safe to implement in that way. But it is recommended to avoid such implementations as it is a source for errors. Within the short example it is easy to understand the source code but in real applications such implementations are a good source for errors.

Furthermore within this example the virtual function is implemented within the base class and the derived class. So we have a valid and running application. But often you explicitly want to move the responsibility for implementation to the derived class. In this case you can create a pure virtual function. The following source code shows the adapted example.

class BaseClass
{
public:
  BaseClass()
  {
    std::cout << "BaseClass ctor" << std::endl;
    LogClassInformation();
  }

  ~BaseClass()
  {
    std::cout << "BaseClass dtor" << std::endl;
    LogClassInformation();
  }

  virtual void LogClassInformation() = 0;
};

class DerivedClass : public BaseClass
{
public:
  DerivedClass()
  {
    std::cout << "DerivedClass ctor" << std::endl;
    LogClassInformation();
  }

  ~DerivedClass()
  {    
    std::cout << "DerivedClass dtor" << std::endl;    
    LogClassInformation();
  }

  void LogClassInformation()
  {
    std::cout << "DerivedClass log" << std::endl;
  }
};


int _tmain(int argc, _TCHAR* argv[])
{
  DerivedClass myClass = DerivedClass();
  
  return 0;
}

Now thinks will become more difficult. What’s the expected behavior now? Of course the dtor and ctor execution order stays the same and therefore the base class logging function is called. But this time it is a pure virtual function and cannot be called. Some compilers will recognize such errors and report a linking issue other compilers will not recognize the issue which will result in an error during runtime.

Summary

Do not call virtual functions during construction or destruction, because such calls will never go to a more derived class than that of the currently executing constructor or destructor. This may result in undefined behavior and is a source of errors.

Advertisements
Veröffentlicht unter C++ | Kommentar hinterlassen

Design patterns: Strategy

The strategy design pattern is used in case different algorithms are implemented and shall vary dynamically dependent on the use case.

Very often, you can implement functionality in different ways. In most use cases you look at the pros and cons of the alternatives and select one. But sometimes you need the flexibility to offer different implementations and select the best one dynamically in context of the actual use cases. For example you want to read and save data in an encrypted format. In this case you may choose and implement a serialization strategy and an encryption strategy. Or you can offer different strategies and let the client application select the ones they need. This flexibility can be implemented by using the strategy pattern. You will implement different algorithms for one purpose. The context specific strategy will be chosen by the client which uses your context specific object.

The following example will show a possible implementation of the strategy pattern. In our application we will have different kinds of lists. These lists shall be sortable. There exist many different sort algorithms, all with their own context specific pros and cons. So you want to have the flexibility to select the algorithm depending on the kind of list you use.

Therefore we create a sort strategy by defining the according interface.

interface ISortStrategy<T>
{
    void Sort(List<T> list);
}

And we implement some algorithms for this strategy.

class ShellSort<T> : ISortStrategy<T>
{
    public void Sort(List<T> list)
    {
        //todo: implement sort algorithm
    }
}

class QuickSort<T> : ISortStrategy<T>
{
    public void Sort(List<T> list)
    {
        //todo: implement sort algorithm
    }
}

class MergeSort<T> : ISortStrategy<T>
{
    public void Sort(List<T> list)
    {
        //todo: implement sort algorithm
    }
}

At next we create a context specific class, in this case a customer list, which wants to use the sort strategy.

interface ICustomer
{
    string Name { get; set; }
}

class Customers
{
    private ISortStrategy<ICustomer> _sortStrategy;
    private List<ICustomer> _customers;

    public Customers(ISortStrategy<ICustomer> sortStrategy)
    {
        _sortStrategy = sortStrategy;
    }

    public void Sort()
    {
        _sortStrategy.Sort(_customers);
    }
}

Depending on their needs, the client application will have the possibility to select the best strategy dynamically. The following source code shows a console application which create the customer object instance and set the sort strategy.

class Program
{
    static void Main(string[] args)
    {
        ISortStrategy<ICustomer> sortStrategy = new MergeSort<ICustomer>();

        Customers customers = new Customers(sortStrategy);

        customers.Sort();
    }
}

In the example code the strategy was set by dependency injection via constructor. Of course, that’s one possible implementation only. You may also have the possibility to use the strategy as parameter of the sort function or to implement it in another way, for example as delegate if the strategy contains a single function only.

Veröffentlicht unter Design Pattern, Designprinzip, Uncategorized | Kommentar hinterlassen

Show costs of Quick & Dirty implementations

I am sure you all know this issue: There are situations where you have to implement something in a quick & dirty manner. You know that this way of implementation is wrong. And you be afraid of or maybe know for sure that this quick & dirty implementation will never be refactored and will life forever. But why do we have this issue in so many projects? Of course there is no easy answer to this question. But one of the reasons may be a lack of understanding of the consequences and costs of quick & dirty implementations. Within this article I want to open your mind for this topic and show you a way to communicate this issue in a proper way to your project owner.

 

What is Quick & Dirty?

At first we have to understand the term “quick & dirty”. So let’s look at an example:

A Release date is near but some features are still not finished and cannot be finished within the given timeline. This can be a planned release which gets late and it is not possible to change timelines because this will create huge costs (penalty or customers get lost to competitors). Or it may be an unplanned release for example to immediately fix a critical bug.

If a release cannot be done as planned you always have different criteria’s to change. Typically these are:

  • Timeline
  • Effort (Manpower)
  • Features
  • Quality

 

In case of quick & dirty releases we speak about forthcoming releases which are planned in near future and cannot be shifted. Therefore the criteria “Timeline” is fixed. Furthermore the criteria “Effort (Manpower)” is also fixed. You cannot add more people. Please remember the well-known maxim: „Put people in a late project will make it even later.“ You can also not change the “Feature” criteria. In our example we need the features to go to the market or in the bugfix example we have to implement the fixes (features).

Therefore we can change the criteria “Quality” only. Quality, in this case, does not mean testing. See the following article for an explanation why. Quality from user point of view means: reliability, usability and efficiency. And quality from product owner point of view means: changeability, transferability, maintenance, flexibility. In most cases the product owner is not willing to reduce the quality on user level. And therefore the only remaining criteria is quality on product owner level. And that means: code quality.

In summary, if we speak about a “quick & dirty” release we speak about a reduction of code quality.

 

Why is it difficult to see the consequences?

Let’s come back to our initial question: Why is it so difficult to understand the consequences and costs of quick & dirty implementations? I think there are two main issues: technical speech and lack of concrete numbers.

A typical attempt of a software developer to explain the disadvantages of a quick & dirty release to a manager will be something like this: “If we do the quick & dirty implementation we will reduce the code quality. Therefore we have to refactor the source code in future”.

Within this statement, there are two main issues. At first, management will not know what the technical term “Refactoring” means. And second, management needs numbers to see the benefits and costs.

 

What will management decide?

If the software developer describes the meaning and consequences of the quick & dirty release like shown above, the manager will get the following information:

  • It is possible to implement the release within the given timeline
  • We do not need any additional manpower
  • All features will be implemented
  • Code quality will be reduced
  • Refactoring is necessary in future

In summary the management will see the concrete benefit (the software release). This benefit is measurable. And the management will hear about some vague disadvantages which are not measurable. And in this case the decision is very easy: The quick & dirty release has to be done and such vague things like refactoring will be ignored. As a result, if you have to implement the next release and tell the management you need more time than normal because of the refactoring, you may risk that everyone is surprised and you cannot get additional time. A loop of some quick & dirty releases will start and topics like refactoring will be pushed further and further into the future.

 

How to escape from the quick & dirty path?

If you want to prevent such a very frustrating sequence of several quick & dirty releases, you have to show the consequences in a measurable manner. So at first, management needs to understand what it means to reduce code quality and what it means to do refactoring. And at second, you have to show the management concrete numbers. You have to show the costs of the refactoring and therefore the cost increase for the next release. And most important: You have to show the cost increase of the next release in case the refactoring will not be done. That’s the most important part because (in my opinion) refactoring is often refused as management will not see any benefit as result of this refactoring.

 

Explain what it means to reduce code quality

Sometimes it is helpful to illustrate a technical topic by an example based on daily life situations. If your project owner or some managers will not understand such technical terms like code quality or refactoring you have to use another way to explain this topic.

You may compare this topic to borrowing money. The current available time to implement the release is not sufficient. So you have to get additional time for feature development. A reduction of the code quality will allow you to borrow this time. You borrow it from yourself as it must be paid back somewhere in the future in a next release. Therefore, implementing a quick & dirty release is like taking a credit. The credit will help you to pay the actual bills. But in future your financial situation will get even worse. In the next period you also have to pay the bills and additional you have to pay the tax for the credit. And of course somewhere in the future you have to pay back the credits itself. Maybe you have to take a second or third credit. In the worst case you will go bankrupt with this procedure.

A quick & dirty release has the same consequences. You will borrow implementation time. Within the next release you have to pay the tax, so the effort to spend for the next release will get higher. Maybe this can be compensated by a next quick & dirty release. At some point in the future you may not be able to work off the accumulated time liabilities and your project will die.

 

Show concrete numbers

As explained previously you have to replace the abstract concept of refactoring and code quality by concrete numbers. A quick & dirty release will influence the costs of the actual release and the next release(s). Therefore you have to respect these costs in both effort estimations.

Let’s say we start with a good code quality and therefore we do not have open refactoring needs. In this case you will create a standard effort estimation for the release and show it to the product owner. For example the release will cost 50 days of effort. With the actual available resources it may not be possible to spend this effort in the given timeline. So the product owner asks if it is possible to create a quick & dirty release and whether this will save enough time to keep the timelines.

In this case you have to adapt your effort estimation. At first you have to estimate the time reduction for the feature implementation. Let’s say we will save 10 days of effort. At second, you have to estimate the consequences for the next release. And unfortunately this step is not done in most project estimations and will create the trouble in projects with quick & dirty releases.

To reduce this possible trouble, your estimation shall already contain the future consequences. Therefore you have to add the following two values: the costs to repair the code and a cost factor if new features have to be implemented on the bad code base. The first value may be easy to estimate. For example the quick & dirty way has saved 10 days of implementation but a lot of workarounds where implemented. To remove this workarounds and implement the features in a proper way it may be necessary to spend 15 days effort. So the costs to repair the code are 15 days. The second value, the cost increase factor, shall be shown as percentage. This value isn’t easy to estimate. But you have the same issue with the standard effort estimation which is not easy too. The quality of effort estimations depends on experiences. The quality of the estimation of the cost increase factor also depends on experiences and may be more or less accurate. But this factor contains a very important message for the product owner. For example in our case we estimated a cost increase factor of 10%. That means, as a result of the quick & dirty release we have a bad code quality and therefore all future features will need 10% additional effort for implementation.

In summary our example estimation will show the following information.

  • The features can be implemented with 50 days of effort
  • Effort can be reduced by 10 days if we do a quick & dirty implementation
  • To repair the quick & dirty implementation in future will cost 15 days of effort
  • If we do not repair the quick & dirty implementation all future implementations will need 10% more effort

Based on these numbers the product owner is now able to compare the cost of a late release to the cost of an in time but quick & dirty release.

As mentioned at the beginning of this paragraph you also have to adapt the time estimation of the next release(s). Let’s say we have implemented our quick & dirty release shown above. And now it is time for the next release. You will start as usual and do a time estimation for the implementation of the features. This time estimation must now be done based on a normal code quality. So do not pay attention to the bad code quality created within the previous release. Do the estimation with respect to a good code quality.

For example you will estimate an effort of 100 days. Now you have to transfer the number from the last estimation to the actual one. So you have to increase the total effort by the cost increase factor. This factor of 10% will now put additional effort to the estimation. And of course you have to show the costs to repair the code base, which was 15 days. Furthermore you have to think whether the two numbers will be changed if the new features were implemented based on the bad code base. This may result in a higher cost increase factor and higher cost to repair the code base. In our example the new features are strongly connected with the bad code base and could also not be implemented in a clean way. By implementing the new features we well add new bad code. So we estimate a new cost increase factor of 15% and the cost to repair the code base will increase to 20 days.

In summary the estimation will contain the following information.

  • The normal effort to implement the features is 100 days
  • The bad code quality will increase the effort by 10% (10 days)
  • If we repair the code we have to spend 15 days
  • If we repair the code the cost increase factor for the actual release and the next release will be decreased to 0% and so it will be removed
  • If we do not repair the quick & dirty implementation the cost increase factor of future releases is increased to 15% and the costs to repair the code will grow to 20 days

Based on this estimation the product owner is able to see the pros and cons of the different possibilities. As this information is shown based on concreate numbers he is able to calculate the benefits and costs of the different possibilities and select the one with the highest business value.

 

Summary

As developer or project leader you don’t like quick & dirty releases. But they will happen. If they are rarely than the consequences are small. But unfortunately in some projects there are regularly quick & dirty releases. And in these projects the problems and unhappiness will noticeable increase over time. A root cause of this issue may be that the project owner does not know the consequences of quick & dirty releases. Therefore it is very important to show these consequences before you start with the implementation. This can be done within the time estimation. The time estimation should show the consequences of the quick & dirty release in concrete numbers. This will allow the project owner to compare the benefits and costs of quick & dirty releases. As a result they may be done rarely as the product owner will now see how expensive they are.

Veröffentlicht unter Projektleitung | Kommentar hinterlassen

Delete commented-out code

You may see it the source code of nearly in every software project: commented-out code. Maybe you ignore and read over it or maybe you ask yourself: Why does this commented-out code exist? Does the creator of this code store it for any reasons? What should I do with this code?

In my opinion the answer is very simple: Delete commented-out code immediately!

Of course, you will delete something someone other has created. And you may hurt this person if you delete his work. Therefore you should try to make a common agreement within your project team: Whenever someone sees commented-out code he shall delete it.

Why does this code exist? I think the only reason is because someone has implemented a change and would temporary store the origin code as possibility to go back to the old version. That’s ok, but as soon as the new implementation is done the old source code is no longer needed. So the commented-out code should not be checked in to the source code control system. If someone wants to go back to the origin version, the code stored in the source code control can be used.

But why is this commented-out code so dangerous? Because the code sits there and rots. It is getting less and less relevant with every passing day. It uses variables which no longer exists, calls functions which have been changed or it follows obsolete conventions. In other words: it is a very code source for errors in case you reactivate the code or use parts of it. Furthermore it may confuse everyone who reads it because everyone will ask itself why this source code exists.

Therefore, in my opinion, you should talk with your team about this topic and make the agreement that everyone who sees commented-out code shall delete it immediately without reading it.

Veröffentlicht unter Uncategorized | Kommentar hinterlassen

Functor, Lamdba and Predicate

Within this blog article I want to explain the C++ functor, lambda and predicate concepts by using them in short and easy to understand examples.

 

Functor

A functor is a class or struct which acts like a function. This is made possible by overloading the () operator. As this function is implemented by using a class, it has all the possibilities of a class. That means you can set parameters during class creation and it can hold state before and between function calls. So it is like a closure in functional languages. Furthermore a functor allows implementing another nice feature, mainly used in functional languages: higher order functions. The functor itself is a higher order function as it returns a function and it can be used in higher order functions which take one or more functions as arguments. Functors and higher order functions are commonly used in STL algorithms.

Let us start with a basic example. We have a vector with integer values and want to increase all values by a fixed or variable number. The STL offers the higher order function “for_each” which can be used to iterate over the vector and execute a function on each element. Therefore we implement a functor which is used to change an element. Within the first example we create a base functor which increases the given value by five.

struct IncreaseBy5
{
  void operator()(int& val)
  {
    val = val + 5;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{  
  auto data = std::vector < int > {3,7,15};
  
  std::for_each(data.begin(), data.end(), IncreaseBy5());

	return 0;
}

 

The class IncreaseBy5 overloads the () operator and can therefore be used like a function. So you can use it within the “for_each” function of the STL.

In the next step we want to use the nice possibilities of functor classes and make our functor more universal. Therefore we allow defining the value which shall be added. This value can set during creation of the functor. The following source code shows the adapted example.

class IncreaseByX
{
public:
  IncreaseByX(int addend) : mAddend{ addend }
  {}

  void operator()(int& val)
  {
    val = val + mAddend;
  }

private:
  int mAddend;
};

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  std::for_each(data.begin(), data.end(), IncreaseByX(10));

  return 0;
}

 

As we have heard before, this closure like concept allows us to access internal parameters during function execution. This will open a wide range of use cases. You may use the internal state of the class and the possibility of changeable class properties to define and modify the behavior of your functor.

Of course, you can also pass more parameters to the functor. The previous example with a variable value could also be implemented with a second function parameter. In such a case you must respect that the “for_each” STL function, as well as many other of STL algorithm higher order functions will accept unary functions only. Our new functor is a binary function as he has two parameters now. A unary function has one parameter only. But we can use the “bind” function to solve this issue. The bind adapter will get a function with several parameters and returns a unary function with one parameter. The following source code shows the according example.

struct IncreaseByX
{
  void operator()(int& val, int addend)
  {
    val = val + addend;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  std::for_each(data.begin(), data.end(), std::bind(IncreaseByX(), std::placeholders::_1, 10));

  return 0;
}

 

STL Functors

As mentioned before, the STL already contains some basic functors. So we don’t have to reinvent the wheel and can use the existing functors. In our example application we have created a function which increases the given value. The STL already offers a nearly equivalent functor: “plus(p1,p2)” which creates the sum of the two given parameters.

With some little modification we can use the existing STL functor within our example. At first we cannot longer use the “for_each” loop because the STL function returns the result instead of use a pointer and change the value directly. Therefore we have to use the “transform” loop to modify the vector values. Furthermore, as the functor is a binary function and we need a unary one, we have to use the bind method once again.

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  std::transform(data.begin(), data.end(), data.begin(), std::bind(std::plus<int>(), std::placeholders::_1, 10));

  return 0;
}

 

For better readability we can create the functor first and use it within the transform method.

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  auto functor = std::bind(std::plus<int>(), std::placeholders::_1, 10);

  std::transform(data.begin(), data.end(), data.begin(), functor);

  return 0;
}

 

Use member function in higher order function

The STL higher order functions like “fore_each” or “transform” expect a functor or static member function as member. But by using “mem_fun_ref” or “mem_fun” it is possible to pass a normal member function as parameter. “mem_fun_ref” is used in case you have an object and “mem_fun” in case you have a pointer. The following example shows how to use a member function as parameter for the “for_each” method.

class Name
{
public:
  Name(std::string name) : mName(name)
  {}

  void Print()
  {
    std::cout << mName << std::endl;
  }

private:
  std::string mName;
};

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < Name >();
  data.push_back(Name("Foo"));
  data.push_back(Name("Bar"));

  std::for_each(data.begin(), data.end(), std::mem_fun_ref(&Name::Print));

  return 0;
}

 

Predicate

A predicate is a functor which returns a Boolean value. Therefore it can be used in higher order functions like “remove_if” which executes their functionality according to the return value of the given functor. Within the next example we want to remove all odd numbers from a given vector. So we create a predicte which checks whether a value is odd and use this predicate within the “remove_if” function. This function returns an iterator from start to end and removes all iterator elements matching the predicate. With this iterator we can use the erase function of the vector and delete the according elements.

class IssOddNumber
{
public:
  bool operator()(int& val)
  {
    return val % 2 ? true : false;
  }

private:
  int mAddend;
};

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15, 22};

  data.erase(std::remove_if(data.begin(), data.end(), IssOddNumber()), data.end());

  return 0;
}

 

Lambda functions

Last but not least I want to show how to use lambda functions to implement the functionality of the example above which increases the vector values. A lambda function is an anonymous function. You can create the function code directly where it is needed without the need of a function declaration. The following example shows how to increase the vector values by 5. Of course you can also pass additional parameters to the lambda function and add a variable value instead of the “5”.

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  std::for_each(data.begin(), data.end(), 
    [](int& val)
    {
      val = val + 5;
    });

  return 0;
}

 

To increase the readability of the code you can create the lambda function first and use it within the for_each loop.

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector < int > {3, 7, 15};

  auto IncreaseBy5 = [](int& val)
  {
    val = val + 5;
  };

  std::for_each(data.begin(), data.end(), IncreaseBy5);

  return 0;
}
Veröffentlicht unter C++ | Kommentar hinterlassen

Use const whenever possible

The const keyword allows you to specify a semantic constraint: an object should not be modified. And compilers will enforce that constraint. This will allow you to communicate to the compiler and to other developers that the object should remain invariant. As a result you code may become more robust, less error prone and even faster as the compiler may become more options for optimizations. Therefore, whenever possible, use the const keyword. What means „whenever possible“? Of course, as developer you know why you implement a method or a variable. So you know whether you need to change the value of a member or if it remains unchanged. And whenever you don not have to change a value you have to declare it as const. For example most method parameters are used as input only and therefore they remain unchanged and can be set to constant.
Outside of classes, you can use the const keyword at global or namespace scope, as well as for objects declared static at file, function, or block scope. Inside of classes, you can use it for both static and non-static data members. That means whenever you declare a variable you can set their behavior to const and therefore mark it as invariant.

Const pointers
Whenever you declare a pointer you have to keep in mind that two kinds of variables are involved. First you have some kind of data and second you have the pointer itself. And each of both elements can be variable or constant. Therefor you have the possibility to define the pointer and/or the data as constant:

char data[] = "Hello world"; 
char* p = data;               // non-const pointer, non-const data
const char* p = data;         // non-const pointer, const data
char* const p = data;         // const pointer, non-const data
const char* const p = data;   // const pointer, const data

Sometimes this syntax is hard to read, especially if you are new to C++ development. To make it easier to understand you can read such a pointer declaration from right to left. For example the third declaration above can be read as: you have a constant pointer p which points to a char.

Const iterators
Iterators are pointers too. So we can expect the same possibilities as above. And in fact you have the same possibilities. You can set whether the data and/or the iterator itself is constant or not. The following example shows the four possible combinations of const/non-const data and pointer in case we using an iterator for a vector.

std::vector<int> data{ 1, 2, 3 };

// non-const iterator, non-const data
std::vector<int>::iterator iter = data.begin();
*iter = 10;   // OK
++iter;       // OK

// const iterator, non-const data
const std::vector<int>::iterator iter = data.begin();   
*iter = 10;   // OK
++iter;       // error

// non-const iterator, const data
std::vector<int>::const_iterator iter = data.begin();   
*iter = 10;   // error
++iter;       // OK

// const iterator, const data
const std::vector<int>::const_iterator iter = data.begin();
*iter = 10;   // error
++iter;       // error

The iterator is a good example for the usage of const. Often you implement a loop over your container data to make data evaluations. In these cases you don’t want to change the data itself and therefore you can use “const_iterator”. But in source code I only see this type of iterator very rarely. So that is a good example why every time you declare a variable you should think about their variability.

Const Member Functions
A member functions marked as const can only be invoked at const objects. Such member functions are important because to work with const objects is a fundamental way to create efficient code. Furthermore, some people argue, that such functions make the interface of the object easier to understand. That’s true as you will now know the function does not change the object. But as the main purpose of an object is to hide the internal memory access behind an interface I would argue that such information is not relevant. If I use an object within a client I don’t want to think about the way the object works internally and in good software design I don’t have to think about this topic. The object itself is a black box. Therefore if you implement a const member function you do it because you want to write efficient code.
Typical scenarios for const member functions are getter ore data evaluation functions. The following example shows a class which holds some data and offers a function for data evaluation. This member function can be declared as const.

class MyClass
{
public:  
  bool IsEmpty() const
  {    
    return mData.empty();
  }

private:
  std::string mData;  
};

A fundamental way to improve code performance is to pass objects by reference to const. And const object instances can call const member functions only. That’s one of the reason why const member function can improve you code performance. The following code shows the previous example, extended by a non const member function and function calls. As you can see, a const object instance can call the const member functions only.

class MyClass
{
public:  
  bool IsEmpty() const
  {    
    return mData.empty();
  }

  void ChangeData()
  {
    mData = mData + "x";
  }

private:
  std::string mData;  
};

int _tmain(int argc, _TCHAR* argv[])
{
  MyClass nonConstInstance;
  const MyClass constInstance;

  nonConstInstance.ChangeData();
  nonConstInstance.IsEmpty();

  constInstance.ChangeData();   // error
  constInstance.IsEmpty();

  return 0;
}

But what does const mean? C++ has the concept of bitwise constness. Therefore compilers will check whether a const member function changes the instance data. Let us test this behavior by changing the source code of the above example and add another member function. We want to count how often the member function is called. So we add a member to store this counter and increase it on function call.

class MyClass
{
public:
  bool IsEmpty() const
  {
    mCounter++;   // error
    return mData.empty();
  }

private:
  std::string mData;
  int mCounter;
};

The compiler will check for bitwise constness and therefore an error is shown as the member mCounter cannot be modified. If we explicitly implement such functionality we can declare the according members as mutable. Mutable members can even be changed in const member functions and therefore they are not included in the bitwise constness check. The following source code shows an according implementation of the previous example.

class MyClass
{
public:
  bool IsEmpty() const
  {
    mCounter++;   // error
    return mData.empty();
  }

private:
  std::string mData;
  mutable int mCounter;
};

The possibility of mutable members speaks for my argument that a client should not think about the internal behavior of an object. Like explained previously some people argue that they like to see const functions in the object interface because they now know that the objects is not changed on such a function call. But as we have seen now, this expectation may not be true.

To make it even more complicate, there is another philosophy behind the constness of an object instance. In contrast to the bitwise constness implemented in C++ you can also think in terms of logical constness. I think it is important to think about these two perspectives. Logical constness means that the object instance is not changed from a logical point of view. As this point of view is always situational if cannot be checked by the compiler but it is an important development guideline. We have seen an according example above. Our class contains members which we explicitly want to change. So our object instance is no longer bitwise const and we explicitly have to disable this check for the according members. But from a logical point of view the object instance stays const as we only have changed some statistical data, maybe needed for debugging or code improvement reasons. Another often discussed aspect are pointers. If your calls contains pointers, you can modify the data referenced by the pointer within a const member function. As you don’t change the pointer itself the bitwise constness rule is not affected. But from a logical point of view this may have a huge impact as you change some base data of the object instance.

In summary const member functions should be used whenever possible as they will allow writing efficient code. For example a fundamental way to improve code performance is to pass objects be reference to const. And const member functions fit seamlessly into this concept. The widespread opinion that const member functions guarantee the constness of the object instance should be seen with caution. As we have seen above there are two kinds of constness. So some people speak about bitwise constness and some about logical constness. And none of these concepts will guarantee that the data of the object is not changed. Therefore the concept of object instance constness should be respected in programming concepts and guidelines. As client developer you only know the interface of the object and you should therefore not make any assumption about the object constness.

Veröffentlicht unter C++ | Kommentar hinterlassen

Dependency Inversion by using the Unity Injection Constructor

One of the five SOLID principles is Dependency Inversion. To implement according to this principle you can use interfaces and dependency injection. As a software application normally consists of hundreds or thousands of objects you will have to set up a complex tree of object instances and their dependencies. This sounds difficult but of course you can use factory methods and abstract factories and this task will become easy. Furthermore you can use dependency injection framework like Unity. These frameworks will manage the creation of object instances and their dependencies.

Within this article I want to show you a classical way to implement dependency injection with an injection constructor. And in the next step we will use Unity which will help to make the implementation a little bit cleaner.

The example application contains three layers of code. We have a management layer which uses an engine layer which uses a controller layer. The application shall calculate a price offer for a product someone wants to order. On controller level we calculate base costs for manufacturing and shipping. On engine layer we calculate the total costs. And last but not least on management layer we pay attention to product or customer specific conditions like discounts.

The complete object instance tree is created by using interfaces and dependency injection.

Let’s start with the controller layer. We create two interfaces. One for the calculation of the manufacturing costs and another one for the shipping costs.

interface IManufacturingController
{
    int CalculateManufacturingCosts(int units);
}

interface IShippingController
{
    int CalculateShippingCosts(int units);
}

And we implement according objects.

class ManufacturingController : IManufacturingController
{
    public int CalculateManufacturingCosts(int units)
    {
        return 5 * units;
    }
}

class ShippingController : IShippingController
{
    public int CalculateShippingCosts(int units)
    {
        return 10 * units;
    }
}

At next we define an engine which shall be responsible to calculate the total price.

interface IPriceEngine
{
    int GetTotalPrice(int units);
}

And we implement this engine. Within this implementation you see the concept of dependency injection by using an injection constructor.

class PriceEngine : IPriceEngine
{
    IManufacturingController _manufacturingController;
    IShippingController _shippingController;
    
    public PriceEngine(
        IManufacturingController manufacturingController,
        IShippingController shippingController)
    {
        _manufacturingController = manufacturingController;
        _shippingController = shippingController;
    }

    public int GetTotalPrice(int units)
    {
        int totalPrice = 0;

        totalPrice += _manufacturingController.CalculateManufacturingCosts(units);
        totalPrice += _shippingController.CalculateShippingCosts(units);

        return totalPrice;
    }
}

The customer manager which adds an additional discount is implemented by using the same principle. The engine is injected as constructor parameter.

interface ICustomerManager
{
    int GetPriceOffer(int units);
}

class CustomerManager : ICustomerManager
{
    IPriceEngine _priceEngine;

    public CustomerManager(IPriceEngine priceEngine)
    {
        _priceEngine = priceEngine;
    }

    public int GetPriceOffer(int units)
    {
        int offer;

        offer = _priceEngine.GetTotalPrice(units);

        if(units >= 100)
        {
            offer = (offer * 9) / 10;
        }

        return offer;
    }
}

Now we can use our manager object to calculate a price offer for the customer. The following source code shows a console application which created the needed object instances and uses the manager to calculate the offer.

static void Main(string[] args)
{
    IManufacturingController manufacturingController = new ManufacturingController();
    IShippingController shippingController = new ShippingController();

    IPriceEngine priceEngine = new PriceEngine(manufacturingController, shippingController);

    ICustomerManager customerManager = new CustomerManager(priceEngine);
            
    Console.WriteLine("offer for 200 units: " + customerManager.GetPriceOffer(200));

    Console.ReadKey();
}

As you can see it is not that difficult to create the dependency tree. But in a real application the tree will get very huge and it will contain redundant branches as you reuse components. You can negotiate this complexity and eliminate redundant creation code by using factory methods and abstract factories. But independent whether you use factories or not you have the possibility to use a dependency injection framework. One of the popular frameworks is Unity. It will allow you to create object instances and their dependencies. For example it supports the injection constructor design we have used in our example.

To use Unity you have to install the according NuGet package. At next you have to mark the injection constructors with an according attribute. The following source code shows the adaptation if the engine and manager classes.

class PriceEngine : IPriceEngine
{
    IManufacturingController _manufacturingController;
    IShippingController _shippingController;

    [InjectionConstructor]
    public PriceEngine(
        IManufacturingController manufacturingController,
        IShippingController shippingController)
    {
        _manufacturingController = manufacturingController;
        _shippingController = shippingController;
    }

    public int GetTotalPrice(int units)
    {
        ...
    }
}

class CustomerManager : ICustomerManager
{
    IPriceEngine _priceEngine;

    [InjectionConstructor]
    public CustomerManager(IPriceEngine priceEngine)
    {
        _priceEngine = priceEngine;
    }

    public int GetPriceOffer(int units)
    {
        ...
    }
}

Now we can use a unity container within the console application. We have to register all interfaces and the according classes. After this registration we can use the Unity container to create an object instance.

static void Main(string[] args)
{
    IUnityContainer unitycontainer = new UnityContainer();
    unitycontainer.RegisterType<ICustomerManager, CustomerManager>();
    unitycontainer.RegisterType<IPriceEngine, PriceEngine>();
    unitycontainer.RegisterType<IManufacturingController, ManufacturingController>();
    unitycontainer.RegisterType<IShippingController, ShippingController>();

    ICustomerManager customerManager = unitycontainer.Resolve<CustomerManager>();

    Console.WriteLine("offer for 200 units: " + customerManager.GetPriceOffer(200));

    Console.ReadKey();
}

As you can see we don’t have to know and to set the dependencies now. We have set all dependencies implicit by defining the injection constructor. Unity will use this information to create and inject all needed object instances.

Of course, Unity offers some more features and possibilities. This little example application shall give you a short introduction into the topic of Dependency Injection and will help you to decide whether you want to use Unity within your project.

Veröffentlicht unter .NET, C#, Design Pattern | Kommentar hinterlassen