11 Frequently Asked Questions by C++ Programmers

The following points are applicable to all C++ programmers. The reason I say they are the most important is because the points mentioned in these points are things you usually won't find in a C++ book or website. Such as: pointers to members, this is a place that many materials do not want to mention, and it is also a place where mistakes are often made, even for some advanced C++ programmers.
  The point here is not just to explain how to write better code, but to show what's in the rules of the language. Clearly, they are a permanent source of good material for C++ programmers. I believe this article will benefit you a lot.

  First, I put together some frequently asked questions by C++ programmers at different levels. I was surprised how many experienced programmers didn't realize whether .h symbols should still be in standard header files.

 

Point 1: Still ?

   Many C++ programmers are still using the newer standard library instead of using it. What's the difference between the two? First of all, 5 years ago we started to oppose the continued use of .h symbols in standard header files. Continuing to use outdated rules is not a good approach. From a functional point of view, <iostream> contains a set of templated I/O classes that, instead, only support character streams. In addition, the C++ standard specification interface for input and output streams has been improved in subtle details and, therefore, differs in interface and implementation. Finally, the components of is declared in the form of STL, while the components of are declared as global.

  Because of these substantial differences, you cannot mix the two libraries in one program. As a habit, it is generally used in new code, but if you are dealing with code written in the past, you can use the old code to keep the code consistent for inheritance.  


Point 2: Points to be aware of when passing parameters by reference

  When passing parameters by reference, it is better to declare the reference as const. The advantage of this is: tell the program that this parameter cannot be modified. In the following example the function f() is the passed reference:
void f(const int & i);
int main()
{
 f(2); /* OK */
}
   This program passes an argument 2 to f() . At runtime, C++ creates a temporary variable of type int with a value of 2 and passes its reference to f(). This temporary variable and its reference are created from the time f() is called and exist until the function returns. When returned, it is immediately deleted. Note that if we do not prepend the reference with the const qualifier, the function f() may change the value of its parameter, more likely to cause the program to behave unexpectedly. So, don't forget const.

  This gist also applies to user-defined objects. You can also add references to temporary objects if they are of const type:
struct A{};
void f(const A& a);
int main()
{
 f(A()); // OK, a temporary A is passed const reference
}

 

Point 3: "Comma Separated" Expressions

 The "comma-separated" expression is inherited from C and is used in for- and while-loops. Of course, this grammar rule is considered unintuitive. First, let's look at what a "comma-separated" expression is.

  An expression consists of one or more other expressions, separated by commas, such as:
 if(++x, --y, cin.good()) //Three expressions
  This if condition contains three commas separated by commas. separate expressions. C++ evaluates each expression, but the result of a complete "comma-separated" expression is the value of the rightmost expression. So the value of the if condition is true only if cin.good() returns true. Here's another example:
int j=10;
int i=0;
while( ++i, --j)
{
 //until j=0, the loop ends, and during the loop, i keeps adding itself
}

 

Point 4, use the constructor of the global object to call a function before the program starts

   Some applications need to call other functions before the main program starts. For example, the transition process function and the registration function must be called before the actual program runs. The easiest way to do this is to call these functions through the constructor of a global object. Because global objects are constructed before the main program starts, these functions will all return results before main(). Such as:
class Logger
{
 public:
 Logger()
  {
   activate_log();//Translator's Note: Call the function you need to run first in the constructor
  }
};
Logger log; //A global instance
int main()
{
 record * prec=read_log();//Translator's Note: Read log file data
 //.. program code
} The
  global object log is constructed before main() runs, and log calls the function activate_log(). Thus, when main() starts executing, it can read data from the log file.

  Without a doubt, memory management is the most complex and bug-prone place in C++ programming. Direct access to raw memory, dynamically allocated storage, and maximizing the efficiency of C++ instructions make it imperative that you do your best to avoid memory-related bugs.
  
Point 5: Avoid complexly constructed pointers to functions

  Pointers to functions are one of the least readable syntaxes in C++. Can you tell me what the following statement means?
void (*p[10]) (void (*)());
   P is an "array of 10 pointers to a function that returns void and points to another function with no return and no operation". This cumbersome syntax is really hard to read, isn't it? You can actually declare a function equivalent to the above statement simply by typedef. First, declare "pointer to a function with no return and no operation" using typedef:
typedef void (*pfv)();
  Next, declare "pointer to another function with no return and using pfv":
typedef void (*pf_taking_pfv ) (pfv);
  Now, declare an array of 10 pointers like the above:
pf_taking_pfv p[10];
  has the same effect as void (*p[10]) (void (*)()). But isn't this more readable!

 

Point 6: Pointers to Members

  A class has two basic types of members: function members and data members. Similarly, there are two types of pointers to members: pointers to function members and pointers to data members. The latter is actually not commonly used, because classes generally do not contain public data members, and are only used when coordinating structs and classes when inheriting code written in C.

   Pointers to members are one of the most incomprehensible constructs in C++ syntax, but it's also one of C++'s most powerful features. It allows you to call a function member of a class without knowing the name of the function. This is a very agile calling tool. Likewise, you can inspect and change data by using a pointer to a data member without knowing its member name.
  pointer to data member

  Although the syntax of pointers to members may confuse you a little at first, you will soon discover that it is just like a normal pointer, except that the * sign is preceded by the :: symbol and the class name. Example: define a pointer to int type:
int * pi;
  define a data member that points to a class of int type:
int A::*pmi; //pmi is a member of int type that points to class A
  You can initialize it like this It:
class A
{
 public:
 int num;
 int x;
};
int A::*pmi = & A::num;
  The above code declares a num member of type int that points to class A and initializes it to this The address of the num member. You can use and change the value of the num member of class A by prefixing pmi with *:
A a1, a2;
int n=a1.*pmi; //Assign a1.num to n
a1. *pmi=5; // assign 5 to a1.num
a2.*pmi=6; // assign 6 to 6a2.num
  If you define a pointer to class A, you must use -> * operator instead:
A * pa=new A;
int n=pa->*pmi;
pa->*pmi=5;

  pointer to function member

  It consists of the data type returned by the function member, the class name followed by the :: symbol, the pointer name, and the function's parameter list. For example: a pointer to a function member of class A (which returns an int):
class A
{
 public:
 int func ();
};
int (A::*pmf) ();
  The above definition means that pmf is a pointer to a function member func() of class A. In fact, this pointer is no different from a normal pointer to a function, except that it contains the class name and the :: symbol.
You can call this function func() anywhere *pmf is used :
pmf=&A::func;
A a;
(a.*pmf)(); //call a.func()
  if you first define a A pointer to an object, then the above operation should be replaced with ->*:
A *pa=&a;
(pa->*pmf)(); //Calling pa->func()
  for a pointer to a function member should consider polymorphism sex. So, when you call a virtual function member through a pointer, the call will be dynamically recycled. Another thing to note, you can't take the address of a class's constructor and destructor.

 

Point 7. Avoid memory fragmentation

   There is often such a situation: every time your application runs, it leaks memory due to memory leaks caused by the program's own defects, and you are repeating your program periodically. crash the system. But what can be done to prevent it? First, use as little dynamic memory as possible. In most cases, you probably use static or automatic storage or STL containers. Second, try to allocate large chunks of memory rather than a small amount at a time. As an example: allocating the memory needed for one array instance at a time, rather than allocating memory for only one array element at a time.

 

Point 8, is it delete or delete[]

  There is an absurd saying among programmers: it is OK to use delete instead of delete[] to delete an array type!
  For example:
 int *p=new int[10];
 delete p; //Error, it should be: delete[] p
  The program above is completely wrong. In fact, an application that uses delete instead of delete[] on a platform may not crash the system, but that's pure luck. You can't guarantee that your application will compile on another compiler and run on another platform, so use delete[] anyway.

 

Point 9. Optimize the arrangement of members

  The size of a class can be changed by:
struct A
{
 bool a;
 int b;
 bool c;
}; //sizeof (A) == 12
   sizeof (A) equals 12 on my computer. This result may surprise you, since the total number of members of A is 6 bytes: 1+4+1 bytes. Where did the other 6 bytes come from? The compiler inserts 3 padding bytes after each bool member to ensure that each member is arranged in 4 bytes for demarcation. You can reduce the size of A by:
struct B
{
 bool a;
 bool c;
 int b;
}; // sizeof (B) == 8
  This time, the compiler only inserted 2 bytes after member c . Because b occupies 4 bytes, it is natural to arrange it as a word, and the size of a and c is 1+1=2, plus 2 bytes, it is just two words. Form permutation B.

 

Point 10. Why is it dangerous to inherit from a class without a virtual destructor?

   A class without a virtual destructor means it cannot be used as a base class. Such as std::string, std::complex, and std::vector are all like this. Why is it dangerous to inherit from a class without a virtual destructor? When you create a related class that inherits from a base class by public inheritance, both pointers and references to objects in the new class actually point to the originating object. Because destructors are not virtual functions, C++ doesn't call the destructor chain when you delete a class like this. For example:
class A
{
 public:
 ~A() // not a virtual function
 {
 // ...
 }
};
class B: public A // wrong; A has no virtual destructor
{
 public:
 ~B()
 {
 // ...
 }
};
int main()
{
 A * p = new B; // looks right
 delete p; // wrong, B's destructor is not called
}

 

Point 11. Declare nested classes as friend classes

  When you declare a nested class as a friend class, put the friend declaration after the nested class declaration, not before.
class A
{
 private:
 int i;
 public:
 class B //nested class declaration first
 {
  public:
  B(A & a) { ai=0;};
 };
 friend class B;//friend class declaration
};

  If you put the friend class declaration before the declaration of the nested class, the compiler will discard the other declarations after the friend class.

Reprinted from: https://blog.csdn.net/ithomer/article/details/5019203

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325918015&siteId=291194637