C Calling Function Again Leads to Termination

At that place are many pitfalls that a C++ developer may encounter. This tin brand quality programming very hard and maintenance very expensive. Learning the language syntax and having expert programming skills in similar languages, similar C# and Coffee, just isn't enough to utilize C++'s full potential. Information technology requires years of experience and great subject field to avoid errors in C++. In this commodity, we are going to have a await at some of the common mistakes that are made past developers of all levels if they are not careful enough with C++ development.

Common Fault #one: Using "new" and "delete" Pairs Incorrectly

No matter how much nosotros effort, it is very difficult to free all dynamically allocated memory. Even if nosotros tin can exercise that, it is often not prophylactic from exceptions. Let us look at a simple example:

          void SomeMethod() {   ClassA *a = new ClassA;   SomeOtherMethod();      // information technology can throw an exception   delete a; }                  

If an exception is thrown, the "a" object is never deleted. The following case shows a safer and shorter mode to do that. It uses auto_ptr which is deprecated in C++11, but the old standard is notwithstanding widely used. Information technology can be replaced with C++eleven unique_ptr or scoped_ptr from Boost if possible.

          void SomeMethod() {   std::auto_ptr<ClassA> a(new ClassA); // deprecated, please cheque the text   SomeOtherMethod();      // it can throw an exception }                  

No matter what happens, afterward creating the "a" object information technology will be deleted equally soon every bit the program execution exits from the scope.

Still, this was simply the simplest instance of this C++ problem. There are many examples when deleting should be done at some other place, perhaps in an outer role or another thread. That is why the utilise of new/delete in pairs should exist completely avoided and advisable smart pointers should be used instead.

Common Mistake #2: Forgotten Virtual Destructor

This is one of the nigh common errors that leads to memory leaks inside derived classes if in that location is dynamic retention allocated inside them. There are some cases when virtual destructor is not desirable, i.e. when a class is non intended for inheritance and its size and performance is crucial. Virtual destructor or whatsoever other virtual function introduces additional data within a class structure, i.e. a pointer to a virtual table which makes the size of whatsoever instance of the class bigger.

Yet, in most cases classes tin be inherited fifty-fifty if it is not originally intended. So it is a very good practise to add a virtual destructor when a form is declared. Otherwise, if a class must not contain virtual functions due to performance reasons, it is a good practice to put a comment within a class declaration file indicating that the class should non be inherited. One of the best options to avert this issue is to utilize an IDE that supports virtual destructor creation during a class cosmos.

One additional point to the bailiwick are classes/templates from the standard library. They are non intended for inheritance and they exercise not have a virtual destructor. If, for example, nosotros create a new enhanced string course that publicly inherits from std::cord there is possibility that somebody will use information technology incorrectly with a pointer or a reference to std::string and cause a retentivity leak.

          class MyString : public std::string { 	~MyString() { 	// ... } };  int main() { 	std::string *south = new MyString(); 	delete s; // May non invoke the destructor divers in MyString }                  

To avoid such C++ problems, a safer way of reusing of a class/template from the standard library is to utilise private inheritance or composition.

Common Mistake #3: Deleting an Array With "delete" or Using a Smart Arrow

Common Mistake #3

Creating temporary arrays of dynamic size is often necessary. Later on they are not required anymore, it is important to free the allocated retentivity. The big problem here is that C++ requires special delete operator with [] brackets, which is forgotten very easily. The delete[] operator will not just delete the memory allocated for an assortment, but it will first call destructors of all objects from an assortment. It is also wrong to utilize the delete operator without [] brackets for primitive types, even though there is no destructor for these types. There is no guarantee for every compiler that a pointer to an array volition bespeak to the showtime element of the array, then using delete without [] brackets can effect in undefined behaviour too.

Using smart pointers, such as auto_ptr, unique_ptr<T>, shared_ptr, with arrays is too wrong. When such a smart pointer exits from a scope, it volition call a delete operator without [] brackets which results in the same problems described to a higher place. If using of a smart pointer is required for an array, it is possible to utilize scoped_array or shared_array from Heave or a unique_ptr<T[]> specialization.

If functionality of reference counting is non required, which is mostly the case for arrays, the near elegant style is to employ STL vectors instead. They don't just take intendance of releasing memory, just offer boosted functionalities besides.

Common Mistake #iv: Returning a Local Object past Reference

This is mostly a beginner's mistake, only it is worth mentioning since there is a lot of legacy code that suffers from this issue. Let'due south look at the following code where a programmer wanted to do some kind of optimization by avoiding unnecessary copying:

          Complex& SumComplex(const Complex& a, const Circuitous& b) {     Complex consequence;     …..     return result; }  Complex& sum = SumComplex(a, b);                  

The object "sum" will now signal to the local object "effect". Merely where is the object "consequence" located afterward the SumComplex part is executed? Nowhere. It was located on the stack, but after the function returned the stack was unwrapped and all local objects from the function were destructed. This will somewhen result in an undefined behaviour, even for primitive types. To avert performance issues, sometimes it is possible to use return value optimization:

          Complex SumComplex(const Complex& a, const Complex& b) {      return Complex(a.real + b.real, a.imaginar + b.imaginar); }  Complex sum = SumComplex(a, b);                  

For about of today's compilers, if a render line contains a constructor of an object the code will be optimized to avert all unnecessary copying - the constructor will be executed directly on the "sum" object.

Common Mistake #5: Using a Reference to a Deleted Resource

These C++ problems happen more oftentimes than you may think, and are usually seen in multithreaded applications. Let us consider the following code:

Thread 1:

          Connexion& connectedness= connections.GetConnection(connectionId); // ...                  

Thread 2:

          connections.DeleteConnection(connectionId); // …                  

Thread 1:

          connection.send(information);                  

In this example, if both threads used the same connection ID this will issue in undefined behavior. Admission violation errors are oftentimes very hard to find.

In these cases, when more than one thread accesses the same resource it is very risky to keep pointers or references to the resource, because some other thread tin delete it. It is much safer to use smart pointers with reference counting, for example shared_ptr from Boost. It uses atomic operations for increasing/decreasing a reference counter, so it is thread safe.

Common Mistake #six: Allowing Exceptions to Leave Destructors

Information technology is not oftentimes necessary to throw an exception from a destructor. Fifty-fifty and so, there is a improve way to do that. Notwithstanding, exceptions are generally not thrown from destructors explicitly. Information technology can happen that a elementary control to log a destruction of an object causes an exception throwing. Let's consider following lawmaking:

          class A { public:    A(){}    ~A()    {       writeToLog(); // could cause an exception to be thrown    } };  // …  endeavor {    A a1;		    A a2;		 } catch (std::exception& e) {    std::cout << "exception caught"; }                  

In the code above, if exception occurs twice, such as during the devastation of both objects, the catch argument is never executed. Because in that location are two exceptions in parallel, no matter whether they are of the same type or different type the C++ runtime environment does not know how to handle it and calls a terminate function which results in termination of a program's execution.

So the general rule is: never allow exceptions to get out destructors. Even if information technology is ugly, potential exception has to exist protected like this:

                      try    {           writeToLog(); // could crusade an exception to exist thrown    }    catch (...) {}                  

Common Mistake #7: Using "auto_ptr" (Incorrectly)

The auto_ptr template is deprecated from C++11 because of a number of reasons. Information technology is still widely used, since nearly projects are still being developed in C++98. It has a certain characteristic that is probably non familiar to all C++ developers, and could cause serious problems for somebody who is not careful. Copying of auto_ptr object will transfer an ownership from one object to another. For example, the following code:

          auto_ptr<ClassA> a(new ClassA); // deprecated, please bank check the text auto_ptr<ClassA> b = a; a->SomeMethod();    // will event in admission violation error                  

… volition result in an access violation fault. Only object "b" volition contain a arrow to the object of Class A, while "a" will exist empty. Trying to access a class member of the object "a" will result in an admission violation mistake. In that location are many means of using auto_ptr incorrectly. Four very critical things to remember about them are:

  1. Never utilise auto_ptr inside STL containers. Copying of containers will go out source containers with invalid data. Some STL algorithms tin can as well lead to invalidation of "auto_ptr"southward.

  2. Never use auto_ptr as a function argument since this will lead to copying, and exit the value passed to the statement invalid after the function call.

  3. If auto_ptr is used for data members of a class, exist sure to make a proper copy inside a copy constructor and an assignment operator, or disallow these operations by making them private.

  4. Whenever possible use some other modern smart arrow instead of auto_ptr.

Mutual Mistake #8: Using Invalidated Iterators and References

Information technology would be possible to write an unabridged book on this subject. Every STL container has some specific atmospheric condition in which it invalidates iterators and references. It is important to be aware of these details while using whatever performance. Just like the previous C++ problem, this one tin can also occur very frequently in multithreaded environments, so it is required to use synchronization mechanisms to avoid information technology. Lets see the following sequential code as an example:

          vector<string> v; v.push_back("string1"); string& s1 = 5[0];     // assign a reference to the 1st element vector<string>::iterator iter = v.begin();    // assign an iterator to the 1st element v.push_back("string2"); cout << s1;     // access to a reference of the 1st element cout << *iter;  // access to an iterator of the 1st element                  

From a logical point of view the code seems completely fine. All the same, calculation the second element to the vector may effect in reallocation of the vector's memory which volition make both the iterator and the reference invalid and outcome in an access violation error when trying to admission them in the last 2 lines.

Common Mistake #ix: Passing an Object past Value

Common Mistake #9

You lot probably know that it is a bad idea to pass objects past value due to its performance impact. Many exit information technology like that to avert typing extra characters, or probably think of returning after to do the optimization. It unremarkably never gets done, and as a result leads to lesser performant code and code that is prone to unexpected behavior:

          grade A { public:    virtual std::cord GetName() const {return "A";}     … };  class B: public A { public:    virtual std::string GetName() const {return "B";}    ... };  void func1(A a) {    std::cord name = a.GetName();    ... }  B b; func1(b);                  

This code will compile. Calling of the "func1" office will create a partial re-create of the object "b", i.east. it will re-create only class "A"'s office of the object "b" to the object "a" ("slicing problem"). So inside the function it volition also call a method from the class "A" instead of a method from the course "B" which is virtually probable not what is expected past somebody who calls the function.

Similar bug occur when attempting to catch exceptions. For case:

          class ExceptionA: public std::exception; class ExceptionB: public ExceptionA;  try {    func2(); // can throw an ExceptionB exception } catch (ExceptionA ex) {     writeToLog(ex.GetDescription());      throw; }                  

When an exception of blazon ExceptionB is thrown from the function "func2" information technology volition exist caught by the catch cake, but considering of the slicing problem but a part from the ExceptionA grade will be copied, wrong method will exist called and also re-throwing will throw an incorrect exception to an outside try-take hold of block.

To summarize, always pass objects by reference, not past value.

Common Fault #10: Using User Divers Conversions by Constructor and Conversion Operators

Fifty-fifty the user defined conversions are very useful sometimes, but they tin pb to unpredicted conversions that are very hard to locate. Allow's say somebody created a library that has a string class:

          course String { public:    String(int north);    String(const char *s);    …. }                  

The first method is intended to create a string of a length northward, and the 2nd is intended to create a string containing the given characters. But the problem starts equally soon as you have something like this:

          String s1 = 123; String s2 = 'abc';                  

In the case above, s1 will get a string of size 123, not a string that contains the characters "123". The second example contains unmarried quotation marks instead of double quotes (which may happen by accident) which will also result in calling of the first constructor and creating a string with a very big size. These are really simple examples, and there are many more complicated cases that lead to confusion and unpredicted conversions that are very hard to find. There are 2 general rules of how to avoid such problems:

  1. Ascertain a constructor with explicit keyword to disallow implicit conversions.

  2. Instead of using conversion operators, apply explicit conversation methods. Information technology requires a little scrap more than typing, but it is much cleaner to read and can help avert unpredictable results.

Conclusion

C++ is a powerful language. In fact, many of the applications that you lot utilize every day on your computer and have come to dear are probably built using C++. As a linguistic communication, C++ gives a tremendous amount of flexibility to the developer, through some of the most sophisticated features seen in object-oriented programming languages. However, these sophisticated features or flexibilities tin often get the cause of confusion and frustration for many developers if non used responsibly. Hopefully this list will help you sympathize how some of these mutual mistakes influence what you lot can attain with C++.


Further Reading on the Toptal Engineering Blog:

  • How to Larn the C and C++ Languages: The Ultimate List
  • C# vs. C++: What's at the Core?

greenalacertut1954.blogspot.com

Source: https://www.toptal.com/c-plus-plus/top-10-common-c-plus-plus-developer-mistakes

0 Response to "C Calling Function Again Leads to Termination"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel