Why write this article

This article is “Android audio and video development beat strange upgrade” series of articles “outside” article, originally intended to write the content of this article in the “Android FFmpeg video decoding play” this article, because to learn FFmpeg related knowledge, C++ basic knowledge is essential.

However, I found that the length was still too long, and some of you may be familiar with C++, so this section is made into an independent part, which is more conducive for those who are not familiar with C++ to learn and view, and those who are familiar with it can also skip it directly.

There are many differences between C++ and Java. For those who have not used C++, C++ is the first hurdle to learn NDK development and must be mastered.

This article marks the most basic and commonly used similarities and differences between C++ and Java for everyone to learn by contrast.

Of course, this article only focuses on the most commonly used knowledge in C++, but also the key knowledge to explain, if there is time, it is best to systematically learn the relevant basic knowledge.

You can read about it in this article

This article uses the contrast way, will C++ and we are very familiar with the Java comparison study, introduced C++ and Java use similarities and differences, to help you quickly start C++.

C++ basic data types

C++ provides the following basic data types

type The keyword
The Boolean bool
character char
The integer int
floating-point float
Double floating point double
No type void

These types can also be modified by type modifiers to extend more data types:

Type modifier The keyword
Signed type signed
Unsigned type unsigned
Short type short
Long type long

Where signed and unsigned specify whether the data is positive or negative; Short and long mainly specify the memory size of the data.

Due to different systems, the amount of memory occupied by the same data type may not be the same. Typical values are as follows:

type Memory size The scope of
char 1 byte -128 to 127 or 0 to 255
unsigned char 1 byte 0 to 255
signed char 1 byte 128 to 127
int 4 bytes 2147483648 to 2147483647
unsigned int 4 bytes 0 to 4294967295
signed int 4 bytes 2147483648 to 2147483647
short int 2 bytes 32768 to 32767
unsigned short int 2 bytes 0 到 65,535
signed short int 2 bytes 32768 to 32767
long int 8 bytes – XXX to XXX
signed long int 8 bytes – XXX to XXX
unsigned long int 8 bytes – XXX to XXX
float 4 bytes – XXX to XXX
double 8 bytes – XXX to XXX
long double 16 bytes – XXX to XXX

As you can see,

The short modifier reduces the memory size of the original type by half;

The long modifier doubles the memory size of the original data type.

Second, the c + + class

C++ is an object-oriented language, and classes are essential. The class definition is pretty much the same as Java.

Java classes are usually declared and defined in the same file xxx.java.

C++ classes are usually declared and defined in two separate files, the.h header and the.cpp file

Define a class

The header file for a class usually looks like this:

// A.h

class A
{
private: // Private attributes
    int a; 
    void f1(a);

protected: // Subclass visible
    int b;
    void f2(int i);

public: // Expose attributes
    int c = 2;
    int f3(int j);
    
    A(int a, int b); // constructor~ (A);// destructor

};
Copy the code

The corresponding class implementation file A.cpp is as follows:

// A.cpp

/** * implements the constructor */
A::A(int a, int b): 
a(a),
b(b) {
    
}

/ / equivalent to the

/* A::A(int a, int b) { this.a = a; this.b = b; } * /

/** * implements the destructor */
A::~A() {
    
}

/** * implement the f1 method */
void A::f1() {

    
}

/** * implement f2 method */
void A::f2(int j) {
    this.b = j
}

/** * implement the f3 method */
int A::f3(int j) {
    this.c = j
}
Copy the code

As you can see, the.h file is responsible for declaring class member variables and methods; The.cpp file is responsible for defining member variables and methods.

However, it is not necessary to implement classes in this structure, you can also implement classes in.hVariables and methods are defined directly in header files.

Such as:

// A.h

class A {
private:
    int a = 1;
    
public:
    void f1(int i) {
        this.a = i; }}Copy the code

A couple of special things about C++ classes

1) Visibility private, protected, public

These keywords are the same as in Java, except that in C++, instead of declaring visibility for each member variable and method, variables and methods with different visibility are grouped together and declared uniformly, as defined in class A above.

2) Constructor and destructor

Class constructors in C++ are basically the same as in Java, except that member variables are initialized in a special way when constructors are implemented. As follows:

A::A(int a, int b): 
a(a),
b(b) {
    
}

/ / equivalent to the

A::A(int a, int b) {
    this.a = a;
    this.b = b;
}
Copy the code

Either of the above methods can be used, and the first method is usually used.

Destructors are not available in Java. Mark with tilde ~.

It is called automatically by the system, just like constructors, except that constructors are called when the class is created and destructors are called when the class is deleted, mainly to free up internal variables and memory.

The destructor is declared as ~ class name ();

The implementation takes the form of class name ::~ class name () {}

See the way class A is written above.

3) : :Double colon

If you look at the class definition above, you must be surprised by the notation ::. This is the C++ field operator used to indicate which fields variables and methods belong to, such as the one above

void A::a() { }
Copy the code

Method A belongs to class A.

It can also be used to call static member variables of a class, such as

//A.h

class A {
private:
    static int a = 1;
    int b;
    void a();
}

//A.cpp
void A::a() {
    b = A::a;
}
Copy the code

Class inheritance

C++ class inheritance and Java are similar, the format is as follows:

Class B: access-specifier A, where access-specifier is the access modifier, which is one of public, protected, or private.

Access modifiers do the following:

Public inheritance: When a class is derived from a public base class, the public members of the base class are also the public members of the derived class. The protected members of the base class are also the protected members of the derived class. The private members of the base class cannot be accessed directly by the derived class, but can be accessed by calling the public and protected members of the base class.

Protected inheritance: When a class is derived from a protected base, the public and protected members of the base become protected members of the derived class.

Private Inheritance (private) : When a derived class has a base class, the public and protected members of the base class become private members of the derived class.

In general, we use public inheritance, which is the same as Java.

Classes can inherit multiple times

In Java, subclasses can inherit from only one parent, but C++ can inherit from more than one parent, separated by commas:

Class < derived class name >:< inheritance 1>< base class name 1>,< inheritance 2>< base class name 2>,... {< derived class body >};Copy the code

C++ pointer

“Pointers” in Java

In Java, there is no concept of Pointers, but in Java, except for basic data classes, Pointers are used in most cases.

For example, the following Java code:

People p1 = new People("David"."0001");
People p2 = p1;
p2.setName("Denny"); System.out.println(p1.getName()); // The output is DennyCopy the code

People p2 = p1; People p2 = p1; After that, P2 and P1 point to the same storage space, so the modification of P2 also affects P1.

So why is there so little attention paid to Pointers in Java?

Because Java encapsulates Pointers, it does not allow explicit manipulation of Pointers, and the memory in Java is managed by the virtual machine without the need to free the requested memory.

Pointers in C++

1) Pointer declaration and definition

Unlike Java, the pointer concept in C++ is very important and ubiquitous.

Pointer: is a variable whose value is the memory address of another variable. That is, a pointer is a variable that points to a memory address.

Pointers can be declared and defined as follows:

int a = 1; // Declarations of actual variables

int *p; // Declaration of pointer variables

p = &a; // the pointer points to a's memory address

printf("Where p points to: %d, where p points to: %d\n", p, *p);

// Output is as follows:
// p points to the address: -1730170860, p points to the address store contents: 1
Copy the code

There are two important symbols in this example: * and &. Among them:

*: has two functions:

I. Use to define a pointer: type *var_name; Var_name is a pointer variable, such as int *p;

Ii. Used to fetch the contents of a pointer: *var_name, e.g. *p is 1.

&: is an address symbol

It is used to get the memory address of a variable. Like a & a; The value of is the memory location of A, that is, the address of A.

The above example may not have a good idea of what Pointers are for, but let’s look at another example.

class A {
public:
    int i;
};

int main(a) {
    
    / / -- -- -- -- -- -- -- -- -- -- -- -- 1
    A a = A(); // a
    a.i = 1;   // Modify the variable in a
    
    A b = a;   // Define the variable b and assign it to a
    
    A *c = &a; // define pointer C to a
    
    printf("%d, %d, %d\n", a.i, b.i, c->i);
    // Output: 1, 1, 1
    
    / / -- -- -- -- -- -- -- -- -- -- -- -- 2
    b.i = 2; // Modify the variable in b
    
    printf("%d, %d, %d\n", a.i, b.i, c->i);
    // Output: 1, 2, 1
    
    / / -- -- -- -- -- -- -- -- -- -- -- -- 3
    c->i = 3; // Modify the variable in c
    
    printf("%d, %d, %d\n", a.i, b.i, c->i);
    // Output: 3, 2, 3
    
    / / -- -- -- -- -- -- -- -- -- -- -- -- 4
    // Print the address
    printf("%d, %d, %d\n", &a, &b, c);
    // Output: -1861360224, -1861360208, -1861360224
    
    return 0;
}
Copy the code

The above example defines a variable, a, and assigns a to a normal variable b and a pointer variable C.

The first time, I is printed as 1 for each of the three member variables;

The second time, I was changed in b. The result was that only B was changed, and neither A nor C was affected.

The third time, I was changed in C, which changed the values of A and C, and had no effect on B;

Finally, the addresses of the three variables are printed. It can be found that the values of A and C are the same, while the addresses of B are different.

Here’s an example:

When assigning a value to a normal variable, the system creates a new independent memory block, such as B. Changes to B affect only itself.

When assigning to a pointer variable, the system does not create a new memory block. Instead, the system points to an existing memory block, such as C. Any changes to C will affect the original variable, such as A.

One other thing to note is,Pointer to the variableReferences to member variables use arrow symbols->, such asc->i; A reference to a member variable by a normal variable uses the dot symbol., such asb.i

2) new and delete

In the above example, c is defined by creating a variable A and then pointing the pointer variable C to A. There is another way to declare and define a pointer variable, and that is to create it dynamically through new.

class A {
public:
    int i;
}

int main(a) {
    A *a = new A();
    a->i = 0;
    
    printf("%d\n", a->i);
    // Output: 0
    
    // Delete pointer variables to reclaim memory
    delete a;
    
    return 0;
}
Copy the code

This is how pointer variables are created dynamically, which is common in C++.

Important note:

Note that the biggest difference between a pointer variable created with new and a variable not created with new is that Pointers created with new require us to manually reclaim memory ourselves, otherwise it will cause a memory leak. Reclaiming memory is done with the delete keyword.

That is, new and DELETE must be called in pairs.

int main(a) {
    A a = A(); // If there is no new, the system automatically reclaims the memory after the main function ends
    
    A *b = new A(); // The system does not automatically reclaim the memory
    
    delete b; // Delete the memory manually
    
    return 0;
}
Copy the code

As you can see, pointer variables in C++ are actually much closer to the way normal variables are used in Java.

C++ references

References are another very important concept besides Pointers. It is also commonly used in C++.

A reference is an alias for a variable, that is, it is another name for an existing variable.

References and Pointers are so similar that beginners can easily confuse the two.

Declaration and definition of references

Let’s first look at how to declare a reference variable.

// Declare a common variable I
int i = 0;

// Declare a reference to j
int &j = i;

j = 1;

printf("%d, %d\n", i, j)

// Output: 1, 1
Copy the code

If you’re familiar with that, it’s ampersand, but it’s not addressing, it’s just marking.

Remember not to be confused with the addressing symbol, which is A *p = &a;

In the example above, the value of j is changed and the value of I is changed. Isn’t that very similar to a pointer?

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

I. No empty reference exists. References must be connected to a valid block of memory.

Ii. Once a reference is initialized as an object, it cannot be pointed to another object. A pointer can point to another object at any time.

Iii. References must be initialized at creation time. Pointers can be initialized at any time.

I and iii are both well understood and must not be initialized to NULL when declaring a reference.

Ii is the most confusing. What does “can’t be pointed to another object” mean?

The difference between reference and pointer

Look at the following example:

int i = 0;

// define a reference to j, pointing to I
int &j = i;

int k = 1;// Does this operation refer to another object?
j = k;

printf("%d, %d, %d\n", i, j, k);
// Output: 1, 1, 1

// Print the address
printf("%d, %d, %d\n", &i, &j, &k);
// Output: -977299952, -977299952, -977299948
Copy the code

You can see that the values of I, j, and K all change to 1, which looks the same as a pointer, but is qualitatively different.

Looking at the last printout, the address of I and j is always the same, not k. In other words, j always points to I, immutable. J = k is just changing the value of k to j, and it’s changing the value of I.

If you don’t understand, look at the pointer example.

int i = 0;

// define pointer j to I
int *j = &i;

int k = 1;

// Point to another object
j = &k;

printf("%d, %d, %d\n", i, *j, k);
// Output: 0, 1, 1

// Print the address
printf("%d, %d, %d\n", &i, j, &k);
// Output: -1790365184, -1790365180, -1790365180
Copy the code

See? After j is assigned &k, the address becomes the same as k, that is, the pointer j can point to different objects. In this case, j has nothing to do with I, and the value of I does not change as j changes.

How to use references

References are most often used as arguments to functions.

void change(int &i, int &j) {
    int temp = i;
    i = j;
    j = temp;
}

int main(a) {
    int i = 0;
    int j = 1;
    
    // Print the address
    printf("[before: %d, %d]\n", &i, &j);
    // Output: [before: -224237816, -224237812]
    
    change(i, j);
    
    printf("[i: %d, j: %d]\n", i, j);
    // Output: I: 1, j: 0
    
    // Print the address
    printf("[after: %d, %d]\n", &i, &j);
    After: -224237816, -224237812
    
    return 0;
}
Copy the code

In the above example, both parameters of the change method are references, which differ from normal parameters in the following two ways:

I. A reference parameter does not create a new block of memory. The parameter is just a reference to a variable passed in from the outside.

Ii. Reference parameters can change the value of external variables.

This is the case for ordinary variables:

void change(int i, int j) {
    int temp = i;
    i = j;
    j = temp;
    
    // Print the address
    pritf("[change: %d, %d]\n", &i, &j);
    [change: -1136723044, -1136723048]
}

int main(a) {
    int i = 0;
    int j = 1;
    
    // Print the address
    printf("[before: %d, %d]\n", &i, &j);
    // Output: [before: -224237816, -224237812]
    
    change(i, j);
    
    printf("[i: %d, j: %d]\n", i, j);
    Output: I: 0, j: 1
    
    // Print the address
    printf("[after: %d, %d]\n", &i, &j);
    After: -224237816, -224237812
    
    return 0;
}
Copy the code

As you can see, the value of I and j will not be changed. The reason is that the change method creates two temporary local variables, each with its own memory block. The address of the variable is unrelated to the external variable passed in, so the value of the external variable cannot be changed.

At this point, you can see the benefits of parameter references: Referencing parameters saves memory and makes execution faster.

Similarly, pointer parameters have a similar effect, but are still fundamentally different from references. References provide us with another great option for passing arguments.

Sometimes, we don’t want to change the value of an external variable inside a function. We can mark the parameter as a constant.

void change(const int &i, const int &j) {
    int temp = i;
    i = j;     // I is not allowed to change, compilation error
    j = temp;  // change j is not allowed, compilation error
}
Copy the code

C++ polymorphism and virtual functions

Polymorphism is one of the three characteristics of object orientation.

C++ polymorphism is very similar to Java, but there are significant differences.

Static binding

Here’s an example:

class A {
public:
    void f(a) {
        printf("a\n");
    };
};

class B : public A {
public: 
    void f(a) {
        printf("b\n");
    };
};

int main(a) {
    A *a = new B();
    a->f();
    // output: a
    
    return 0;
}
Copy the code

Here B inherits A and rewrites method F.

In the main function, we define a base variable pointer A that points to subclass B. It then calls a’s method f.

If it were a similar operation in Java, it would, of course, output B here, but output A here. In other words, here method F is actually f of base class A.

This is one of the big differences between C++ and Java.

The reason is that the calling function f() is set by the compiler to its version in the base class, which is known as static polymorphism, or static linking.

Function calls are prepared before the program executes. This is sometimes referred to as early binding because the f() function is set up during program compilation.

What if you want to implement something like polymorphic overloading in Java?

Virtual functions

Virtual is a C++ keyword used to declare functions and represent virtual functions. Used to tell the compiler to link dynamically instead of statically.

As in the above example, adding virtual to the f function of A gives A Java-like effect:

class A {
public:
    virtual void f(a) {
        printf("a\n");
    };
};

class B : public A {
public: 
    void f(a) {
        printf("b\n");
    };
};

int main(a) {
    A *a = new B();
    a->f();
    // Output: b
    
    return 0;
}
Copy the code

Pure virtual function

In Java, we often use interface or abstract to define interfaces for code specification and extension, but there is no such method in C++, but there can be a similar implementation: pure virtual functions.

class A {
public:
    // Declare a pure virtual function
    virtual void f(a) = 0;
}

class B : public A {
public: 
    // Subclasses must implement f or they will not compile
    void f(a) {
        printf("b\n");
    };
};

int main(a) {
    A *a = new B();
    a->f();
    // Output: b
    
    return 0;
}
Copy the code

Void f() = 0; It’s a pure virtual function. If you inherit from A, the subclass must implement the f interface or it will not compile.

A is an abstract class. Cannot be used directly by definition.

C++ preprocessing

There is a method in C++ that allows you to do something with your code before it is compiled. It is called preprocessing. This is not available in Java, but is often used in C++.

Preprocessing is instructions that are notC++Statement, so a semicolon is not required;The end.

All pre-processed statements start with a hash sign #.

For example, #include is a preprocessing used to import other files into one another, similar to Java import.

For example, import header files:

// A.h
class A{
public:
    A();
    ~A();
}
Copy the code
#include "A.h"

A::A() {
    
}

A::~A() {
    
}
Copy the code

inC++There are several commonly used preprocesses in#include,#define#if,#else#ifdef#endifAnd so on.

Macro definition

One of the most commonly used preprocessing statements, #define, is often called macro definition.

Its form is:

#define name replacement-text 
Copy the code
#define3.14159 PI?

printf("PI = %f", PI);

// Before compiling, the above statement is expanded to read:
// printf("PI = %f", 3.14159);

Copy the code
  • Macro definition with arguments
#define SUM(a,b) (a + b)

printf("a + b = %d", SUM(1.2));

// Before compiling, the above statement is expanded to read:
// printf("a + b = %d", 1 + 2);
// a + b = 3
Copy the code
  • ## #The operator

In macro definitions, # is used to string arguments.

#define MKSTR( x ) #x

printf(MKSTR(Hello C++));

// Before compiling, the above statement is expanded to read:
// printf("Hello C++");
// output: Hello C++
Copy the code

In macro definitions, ## is used to concatenate parameters.

#define CONCAT(a, b) a ## b

int xy = 100;

printf("xy = %d", CONCAT(x, y));

// Before compiling, the above statement is expanded to read:
// printf("xy = %d", xy);
// output: xy = 100
Copy the code

Note:## #This can cause unexpanded problems when multiple macro definitions are used nested

Such as:

#define CONCAT(x, y) x ## y

#define A a

#define B b

void mian(a) {

    char *ab = "ab";
    char *AB = "AB";
    
    printf("AB = %s", CONCAT(A, B));
    
    // Before compiling, the above statement is expanded to read:
    // printf("AB = %s", AB);
}
Copy the code

When ## is encountered in CONCAT, the two macro definitions of A and B are not opened. Instead, they are concatenated as two parameters.

So how do you solve this problem? That’s one more layer.

#define _CONCAT(x, y) x ## y
#define CONCAT(x, y) _CONCAT(x, y)

#define A a

#define B b

void mian(a) {

    char *ab = "ab";
    char *AB = "AB";
    
    printf("AB = %s", CONCAT(A, B));
    
    // Before compiling, the above statement is expanded to read:
    // printf("AB = %s", _CONCAT(a, b));
    // printf("AB = %s", ab);
    // Output: AB = AB
}
Copy the code

Conditional compilation

The combination of #if, #else, #ifdef, and #endif is primarily conditional compiled.

Conditional compilation is also often used in C++ to control which code is compiled and which is not.

#define DEBUG

int main(a) {

#ifdef DEBUG
    // participate in compiling
    printf("I am DEBUG\n");
#else
    // Do not participate in compiling
    printf("No DEBUG\n");
#endif

    return 0;
}

// Output: I am DEBUG
Copy the code

#ifdef DEBUG = true printf(“I am DEBUG\n”); .

Printf (“No DEBUG\n”) is compiled if #define DEBUG is removed; .

int main(a) {

#if 0
    // This code is commented out and does not participate in compilation
    printf("I am not compiled\n");
#endif

    return 0;
}
Copy the code

Seven,

Above, basic is often used in C++, similar to Java, but there are some differences in the basic knowledge, because of the object-oriented language has a certain similarity, I believe that after the above foundation, you can read some C++ code unobstructed.

If you are a Java programmer, you may be confused by some of the knowledge, this time you need to abandon some of the conventional thinking in Java, a taste of C++, can actually tap into the code to digest this knowledge, only the real knowledge.

Two websites are recommended:

C++ rookie tutorial

C++ online compilation