For more blog posts, see the complete catalog of the romantic carriage of audio and video systems learning

The previous several blog about THE C language, very little about the grammar, the focus is on memory Pointers, mainly because the GRAMMAR of C language is relatively simple, for programming old drivers may be very familiar with (even if you have not learned the C language), say it is nonsense. But for c + + syntax, programming and old drivers may not be so calm, so about the interpretation of the c + +, talk about a lot of grammar points, c + + c + + syntax content is very large, of course, a few articles can not speak out, can only from the elite to speak, and strive for more direction to explain the nature of, I’ll assume you are written in Java, That is, to a Java program ape learning C++ point of view to write, because Java can be seen as a simplified version of C++ (C++ -), so I believe you old programmers for some details of the grammar content has not words (no big deal Google quickly fix). Hold on, everybody. Let’s move on.

C++ vs. C upgrade

The so-called C++, as the name implies, is on the basis of C++, that is, the upgraded VERSION of C language, where is the upgrade, the first, is object-oriented programming ideas. We always talk about object orientation, but what exactly is object orientation? Many programmers may not have a clear understanding of it.

C language is a typical process-oriented language (although programs with object-oriented thought can also be written with struct). As the name implies, the main idea of programming programs is implemented step by step in accordance with a process. Process-oriented mainly encapsulates the process based on functions, so the reuse of codes is generally based on functions. A function is essentially a wrapper around a series of operations, so a process-oriented language executes one function at a time until the function is complete.

And object-oriented programming, nature or actually perform a function, but the core is that will be executed no original data encapsulation type for each type, this and C struct is actually the same, but the type of object oriented programming is a function (usually called methods), namely function belongs to a type All the, Such data state and operating state of the function of binding together, so you have a specific and vivid kind things, just like a bird with wings “data” and have to fly this “function”, the tiger has developed arm “data state” and have to fight the “function”, classification is more clear and more in line with the habits of thinking. Why is it more consistent with the thinking habit of the person? Because with object-oriented programming, because the states and methods of different types of things are encapsulated so well, programmers can make programming a lot like building a computer, putting together a mainframe, an explicit, and a keyboard.

Object-oriented programming is even more powerful with the ability to inherit, which in turn generates the ability to polymorphism, making the code more extensible, the code is more organized, and between classes and relationships are clearer. (For example, let the geese, sparrows and eagles inherit birds. Once the birds need to add functions, add the geese, sparrows and eagles to the birds automatically, and the place where the call can also be passed as birds, and then execute the corresponding method when one of the geese, sparrows and eagles is passed into the operation. In this way, the call and the call are equivalent to being decoupled), and then deduce the design pattern and other various will apply the polymorphism to the perfect “strange technology and clever”, the project’s scalability and reusability has been greatly enhanced. In the case of the computer assembly above, the mainframe, explicit and keyboard can be flexibly assembled using different brands, and they are only recognized as wire interface, not tied to a fixed brand.

This qualitative change in programming thinking is of great help to writing large-scale projects in the future, and it is really a great contribution to saving the cost of coding and debugging and fixing bugs.

Classes and objects:

Classes and objects are familiar to Java veterans, but the way C++ writes classes and objects is very similar to Java, but there are some subtle differences.

According to the convention, C++ defines a class in a header file, only declares its own methods, and then implements the class’s methods in the corresponding source file. For example, define the class People in people.h:

class People {
// Permissions are slightly different in Java. Write all the members of this permission under this permission
// Add permissions to member variables
private:
    int age;
	char* name;
	
    int getAge(a);
public:
    int money;
    // constructor
    People(int age,int money, char* name);
    
    void show(int money);
};
Copy the code

Implement class methods in people.cpp:

// People:: before each method indicates which class the method belongs to
People::People(int age,int money, char* name) {
    cout << "People(int age, int money, char* name)" << endl;
	this->age = age;
    this->money = money;
    this->name = name;
}

void People::show(int money) {
    cout << "People::show" << money<< money;
}

int People::getAge(a) {
    return age;
}
Copy the code

To use this class, simply import the corresponding header file people.h.

Java creates objects directly with a new, but C++ is slightly different. There are two ways to create objects:

People *people = new People(1);
Copy the code

This creates objects in the heap just like In Java. Java actually uses this type, and we can think of Java references as simplified Pointers. The created object exists globally, so it can be used outside of the function that created it.

Creating an object can be understood as creating an anonymous object that requires a pointer pair to operate on.

New allocates memory to the heap as malloc does. Unlike Java, where malloc is free, new also allocates memory manually, using the delete keyword. If not freed, memory will remain in the heap until the end of the program, creating a classic memory leak (that is, useless objects or data occupy memory).

This object pointer calls a method in the following way:

people->show(1);
Copy the code

There is another object creation approach that Java veterans may not be familiar with:

People people(1);
Copy the code

In this way, objects are created on the stack and are automatically freed as the function finishes execution, meaning that they cannot be used outside of the function that created them. (If you’re not familiar with stacks and heaps, check out this previous post: Ramshackle C memory Management)

In this way, the object created can be seen as having a name, which is directly used by the name of the object.

This object pointer calls a method in the following way:

people.show(1);
Copy the code

Compared with the two methods, the first one is more flexible. It can use mechanisms such as polymorphism to make the code more flexible, and the object use range is large, but the memory allocation efficiency is low. The second method is more efficient in allocating memory, but has a limited range of objects (within the current function).

A constructor

When it comes to classes and objects, it is necessary to mention constructors, but C++ and Java are basically the same, not the same parts, but the differences, mainly the addition of an initialization list syntax sugar and sealing class constructors.

Initializing the list is simple, just put the top

People::People(int age, int money, char* name) {
    cout << "People(int age, int money, char* name)" <<  endl;
    
    this->age = age;
    this->money = money;
    this->name = name;
}
Copy the code

To:

People::People(int age, int money, char* name) :age(age),money(money),name(name), wife(age,name){
    cout << "People(int age, int money, char* name)" <<  endl;
}
Copy the code

Quite simply, there is nothing to say. A sealed class is a class that has members that are objects of a class, rather than a simple primitive type, such as adding a class Wife:

class Wife{
private:
    int age;
    char* name;

public:
    Wife(int age,char* name);
    
    Wife(int age);
};
Copy the code

Then add a member to People:

class People {
private:
    int age;
    char* name;
    //People holds an object of another class internally
    Wife wife;
    
    int getAge(a);
public:
    int money;
    People(a);People(int age);
    
    People(int age,int money, char* name);

    ~People(a); };Copy the code

The constructor for People needs to explicitly call the Wife constructor to initialize the Wife object, as follows:

// Notice the initialization of wife at the end of this line
People::People(int age, int money, char* name) :age(age),money(money),name(name), **wife(age,name)**{
    cout << "People(int age, int money, char* name)" <<  endl;
}
Copy the code

This is different from Java, where member objects are not required to be initialized at the same time the current class object is initialized. They have the flexibility to be initialized at any time they need to be.

The destructor

When it comes to memory management, the creators of C++ and Java have very different basic philosophies. The creators of C++ thought that memory management was so important that it needed to be left to the programmers themselves, and the creators of Java thought that memory management was so important that it needed to be managed automatically by a program (the JVM’s garbage collection mechanism).

Because C++ programmers manage memory manually, once an object is deleted, resources such as objects held in the object’s memory must also be released, otherwise there is a risk of memory leaks. Because Java has automatic memory management, there is no need for destructors to manually clean up memory. Java is not completely free of destructors. Java’s Finalize function also has the function of Destructor and will be called before objects are recycled. See the articles Destructor in Java and Java Destructor

C++ destructors, which exist by default for each class, are called whenever the object is reclaimed (the stack object is out of scope or the heap object is deleted), but if the class contains Pointers to objects in the heap, the destructor needs to be written manually to clean up those objects.

The destructor is of the form “~” before the no-argument constructor, and the destructor must be no-argument because there is no need to pass parameters. For example, the destructor for the People class above is:

~People(a);Copy the code

We can see that People has a string object to which the name pointer points, so we implement the source heap destructor as follows:

People::~People() {
    cout << "~People()" << endl;
    delete name; 
}
Copy the code

When the People object is recycled, the destructor will clean up the object referred to by name. Otherwise, the object referred to by name will eat up memory until the end of the program, unless it is held by an external pointer to it and freed by the external.

Run ~ err, error reported. Run delete name in ~People(); Is an error, why, oh, the original is to delete a can’t release the memory area, because the char pointer is stored in the string constants of constant area, the life cycle for the entire program lifecycle (specific visible rambling C memory management), so can’t release (of course, that is just showing a pit, I hope you don’t step on).

Now let People hold a pointer to memory in the heap, such as the Wife object pointer above, and add a destructor to the Wife class:

Wife::~Wife() {
    cout << "~Wife" << endl;
}
Copy the code

Then add to the People member variable:

Wife *wifePtr = new Wife(20);
Copy the code

Then the People destructor is changed to:

People::~People() {
    cout << "~People()" << endl;
    **deletewifePtr; * *}Copy the code

Run:

~People()

~Wife ~Wife

Why did we print 2 “~Wife” (i.e. call the destructor for Wife 2 times) when we delete Wife once?

One other thing to note here is that there are two Wife member objects in the member variable, the wifePtr pointer and the Wife object itself. In C++, the declaration cycle of the member object itself follows the life cycle of the outer class object, similar to how a function creates an object on the stack and automatically releases it when the function call ends. So when the People object is freed, the Wife object is also freed automatically.

So if object A holds object B and object B holds object C, then release B in the destructor of A and release C in the destructor of B, forming A recursive call that calls release layer by layer to ensure that objects that are no longer used can be released in A timely manner.

reference

Speaking of references, Java old drivers can be said to be have contact with every day, in Java, access can only be accessed through reference to an object, a reference can also point to different objects in the running, including statements and statement referenced classes and same subclass object of the class, c + + reference and have a lot in common, but there are subtle, As mentioned before, a Java reference is similar to a simplified version of a pointer, so a C++ reference can be regarded as a more simplified version of a pointer than a Java reference. Compared with C++, the designer also considered that C++ Pointers are too flexible and easy to introduce code risks, so he designed the reference.

What exactly is a quote? In a word: aliases for variables. The use of references is simple:

int main(a)
{
  int x = 10;
  
  // ref is a reference to x.
  int& ref = x;
  
  // Value of x is now changed to 20
  ref = 20;
  cout << "x = " << x << endl ;
  
  // Value of x is now changed to 30
  x = 30;
  cout << "ref = " << ref << endl ;
  
  return 0;
}
Copy the code

Run as follows:

x = 20

ref = 30

In the above program, ref is a reference to x, so an operation on ref is the same as an operation on x, and vice versa.

The role of reference

Modify values outside the function by reference

So what’s the magic of this nickname? It has been mentioned in the section of “Modifying external variable values of a function inside a function through a pointer” in the ramble on pointer (I) of C language. One function of a pointer is to modify external variable values of a function inside a function through a pointer. Specific examples are as follows:

Because the function call will copy the parameters to the stack, so if the x itself is directly passed, the value of the local variable P in the changeValue function is changed, and P is a completely independent variable from the external x, so the external X will not be affected.

int changeValue(int p){
    p= 9;
    return p;
}

int main(a){
    int x = 10;
    changeValue(x);
    printf("x: %d\n", x);
}
Copy the code

But if we pass the argument through a pointer instead, we can change it to external x:

void changeValue(int* p){
    *p= 9;
}

int main(a){
    int x = 10;
    changeValue(&x);
    printf("x: %d\n", x);
}
Copy the code

The parameter p is changed to a pointer here. The pointer is copied, but it also points to an external x, so x can be changed successfully.

The changeValue method can be changed to use a reference:

int main(a){
    int x = 10;
    // call x directly
    changeValue(x);
    printf("x: %d\n", x);
}
// Use the reference directly when passing the parameter
void changeValue(int &p){
    p= 9;
}
Copy the code

Reference p is the alias of x passed in. If it is an alias, it is x itself.

Why is it possible to modify with a reference? References are essentially Pointers, so I mentioned earlier that references can be viewed as a simplified version of Pointers (simpler than Java’s reference HIA).

Avoid copying large object arguments by reference

Therefore, like Pointers, passing parameters by reference can also be used to prevent large object copy caused by passing parameters as large objects to improve memory efficiency. (Refer to the section “Pointer variables as function parameters” in C Pointers (2).)

// If People is used as a parameter, p will be copied once. If People is large, then time and space will be expensive
void doSomething(People p){
    //doSomething
}

// Change to a reference to People, just like a pointer, without copying the People object passed in
void doSomething(People &p){
    //doSomething
}
Copy the code

The copy constructor is passed as a parameter to the copied object

More on this later, essentially avoiding copying parameters, which can cause not only performance problems, but also errors.

So what’s the difference between a reference and a pointer?

1. References must be initialized at definition time, but Pointers are not:

int &p=a;  //it is correct
   but
int &p;
 p=a;    // it is incorrect as we should declare and initialize references at single step.
Copy the code

2. A reference, once initialized, cannot be redirected to another variable as a reference to another variable, while a pointer can be reassigned:

   int x1 = 10;
   int x2 = 10;
  
    // ref is a reference to x.
    int& ref = x2;
    
    // we want ref to point to x1
    ref = x1;
   
    cout << "x1 = " << x1 << endl ;
    cout << "x2 = " << x2 << endl ;
    x2 = 30;
    cout << "ref = " << ref << endl ;
Copy the code

Running results:

x1 = 10

x2 = 10 ref = 30

We can see that ref = x1; It doesn’t make ref point to x1, it just makes x1 assign to x2, and x2 = 30; It still changes the value of ref, so ref is always loyal. Once ref declares allegiance to one variable, it is no longer loyal to other variables (similar to pointer constants).

3. References cannot point to NULL, while Pointers can:

// Direct compilation error
int &rr = NULL;
Copy the code

Based on the above points, we can see that a reference can be used as a simplified, more secure pointer, so Pointers can be used wherever references are used, but not the other way around. For example, references cannot be used to implement data structures such as linked lists (because they cannot be redirected to other variables). So use references where they can be used in general, and Pointers if not.

Inline function

To call a function, stack frames must be opened and arguments, local variables, return addresses, and several registers must be pushed onto the stack before the code in the function body can be executed. After the code in the function body is executed, the site must be cleaned up, and the data previously pushed onto the stack must be removed before the code after the function call can be executed. So calling a function has time and space overhead, and if the function itself has very little code, it may cost more to call the function than to execute the function itself, which is obviously not reasonable.

So inline functions come into being. The so-called inline function is a function that expands the code to the call point like macro expansion at compile time. It is equivalent to that the function does not exist at run time. The function body is directly written at the call point, so it saves the overhead of the function call.

In C++, we specify that a function is inline by using inline at the function definition (not the declaration).

// Inline function definitions
inline void func(a){
    cout<<"inline function"<<endl;
}
Copy the code

In this way, we suggest to the compiler that the function func be specified as an inline function, and if the compiler takes our advice, it will be used everywhere the compiled code calls func

 cout<<"inline function"<<endl;
Copy the code

Alternative.

Speaking of inline functions, Java veterans may not be familiar with it. Java does not have a keyword that can handle inline functions in the code, so it is easy to think that Java does not have inline functions, but I looked up the Oracle documentation. Understanding Java JIT Compilation with JITWatch, Part 1: “Some JIT Compilation Techniques”

One of the most common JIT compilation techniques used by Java HotSpot VM is inlining, which is the practice of substituting the body of a method into the places where that method is called. Inlining saves the cost of calling the method; no new stack frames need to be created. By default, Java HotSpot VM will try to inline methods that contain less than 35 bytes of JVM bytecode.

So Java’s JIT compilation technology determines whether or not to specify an inline function based on the size of the method, so this step is handled automatically during compilation and is transparent to the programmer.

Pits used by inline functions

As mentioned above, it is common to declare functions in header files and implement them in the corresponding source files, but this can cause problems for inline functions. If you do not know how to expand inline functions, you may not know what to do.

If we did what we did above, we would add an inline function run to the People class above, declaring run in the people.h header as follows:

class People {
private:
    int age;
    int getAge(a);
public:
    int money;
    People(int age);
    
   void show(int age);
    
    // Add the inline function run
    **inline void run(a); * *};Copy the code

Then implement the method in the corresponding source file people.cpp:

inline void People::run(a) {
    cout << "run" <<  endl;
}
Copy the code

Call this method in main.cpp:

people2.run(a);Copy the code

Result link when hang:

In function main':undefined reference to People::run()’

Why is there an error in the link? Undefined reference = undefined reference = undefined reference

The reason for this is that the inline function is used to replace the function call during compilation, and the function no longer exists after compilation, whereas C++ compilation is for a single file. If no call to run is found in people. CPP, the compiler ignores the inline function as if it was not called. Therefore, main. CPP will not find this method at link time, because it does not exist at link stage.

C++ and C mixed

C++ is an upgraded version of C, its two naturally different from the general relationship, just like brothers, that the two can be together to complete a program, that is, mixed programming? Of course, it is possible, because after the precipitation of history, a lot of classic libraries are written in C, if C++ can not be mixed with C language, it is greater than the loss of Jingzhou, so the creator of C++ has taken into account this situation, so that C and C++ can be mixed. However, there are some differences between C++ and C in compilation and linking.

Methods in C++ classes, for example, implicitly add the this pointer as a method parameter to the method argument list at compile time. Also, C++ “renames” overloaded functions at compile time, and the compiler determines which method to call based on the arguments passed to the method call (they are essentially different functions with different entry addresses). On the compilation of different causes, mixed C + + call C will appear in the link stage can’t find the method of error, because the two sides of the same method to compile the results are different (is simply on the same definition, C + + and C language compiled a set of each, so can’t find in the link stage, because of not). So a mix of two requires some extra processing.

Here’s a simple example: add a method to test1.h:

void exchange(int *a, int *b);
Copy the code

Then implement this method in test1.c:

void exchange(int *a, int *b){
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
Copy the code

This method is then called on main. CPP main and returns an error:

Linking CXX executable untitled1.exe

CMakeFiles\untitled1.dir/objects.a(main.cpp.obj): In function main': F:/projects/CppDemo/untitled1/main.cpp:156: undefined reference to exchange(int*, int*)’

This is missing because in test1.c, the compiler will compile the method as C, and in main. CPP, the compiler will compile it as C++, making it look like two different methods and missing the method definition.

Extern “C” and extern “C++” function declarations” are extern “C” and extern “C++” function declarations” are extern “C” and extern “C++” function declarations” are extern “C” and extern “C++” function declarations” are extern “C” and extern “C++” function declarations”

In C++, when used with a string, extern specifies that the linkage conventions of another language are being used for the declarator(s). C functions and data can be accessed only if they’re previously declared as having C linkage. However, they must be defined in a separately compiled translation unit.

In simple terms, is the C language variable or a function defined on “extern” C “” under the scope of, can be linked to the C + +. Extern “C” = extern; extern “C” = extern;

#ifdef __cplusplus 
// if C++ calls, go to the if branch
extern "C" void exchange(int *a, int *b);
#else
// Non-c calls follow this branch
void exchange(int *a, int *b);
#endif
Copy the code

Extern “C” is not extern “C” except for C++, so check if a “__cplusplus” macro is defined during precompilation and use extern “C” only if it is. __cplusplus is a built-in C++ precompiled macro, as shown in the table in the “Predefined macro names” section of the Preprocessor directives:

An integer value. All C++ compilers have this constant defined to some value. Its value depends on the version of the standard supported by the compiler:

Run the program no problem ~

This can also be optimized with precompiled if statements for brevity:

#ifdef __cplusplus
extern "C" {
#endif
void exchange(int *a, int *b);
#ifdef __cplusplus
}
#endif
Copy the code

conclusion

This blog is mainly talked about a few C + + relative to the main point of upgrade (different) C and both how to mix, of course, still have a lot of different relative to C, C + + for instance method overloading, the default parameters, this pointer, static members and methods, and so on, these are a cinch for Java old driver, this level grammar is no fee to speak, And we’ll talk about that later. The next article takes a closer look at object orientation in C++.