Ten thousand feet of tall buildings rise from the ground – Introduction to C++ (Volume 1)

Getting Started with C++

😏Foreword

Hello, everyone, my name is Lu Jiumaru. Today I will start a new chapter in C++. I wonder if you are ready or not. There are still some small parts in the data structure series, but most of the content has been completed. , I will make a relevant summary of the preliminary data structure in the future, so that you can read and watch it. I believe everyone knows the importance of C++ as a programming language. Try to be as comprehensive and detailed as possible. You don’t need to worry about the content. Maruwan will ensure the quality and quantity. Well, I won’t elaborate too much on the redundant content. There are many details in C++. Dig deep into the discussion to help everyone lay a good foundation as much as possible. Don’t talk nonsense, just start!

If you have any questions in the process of reading my blog or learning or in the direction of learning or want to communicate with me, you can add my penguin number: 2361038962 \color{red}{2361038962} 2 3 6 1 0 3 8 9 6 2 , or send an email to the corresponding mailbox: 2361038962 @ q q . c o m \color{red}{2361038962@qq.com} 2 3 6 1 0 3 8 9 6 2 @ q q . c o m , I will try my best to help you with the answer!

🧑1.C++ keywords

C++ has a total of 63 keywords, including 32 keywords in C language:

asm do if return try continue
auto double inline short typedef for
bool dynamic_cast int signed typeid public
break else long sizeof typename throw
case enum Mustable static union wchar_t
catch explicit namespace static_cast unsigned default
char export new struct using friend
class extern operator switch virtual register
const false private template void true
const_cast float protected this volatile while
delete goto reinterpret

🧑2. The first C++ program

#include<iostream>
using namespace std;
int main()
{
    cout << "hello C++" << endl;
    return 0;
}

🧑3. Namespace

In C/C++, variables, functions and classes to be learned later exist in large numbers, and the names of these variables, functions and classes will all exist in the global scope, which may cause many conflicts. The purpose of using namespaces is to localize the names of identifiers to avoid naming conflicts or name pollution, and the namespace keyword appears to address this problem.

Example of a naming conflict:

The definition of sqrt function already exists in the library function <math.h>. At this time, we define a global variable sqrt. At this time, the phenomenon of sqrt redefinition occurs, which is a naming conflict. Of course, naming conflicts not only include this one, but also include naming conflicts between global variables and global variables, naming conflicts between local variables and local variables, and naming conflicts between global functions and global functions . In conclusion,Name conflicts will occur if variable names or function names with the same scope are the same.

πŸ’–3.1 Namespace Definition

To define a namespace, you need to use the namespace keyword , followed by the name of the namespace , and then followed by a pair of {}, where {} is the member of the namespace.

πŸ’˜3.1.1 Normal namespace

πŸ’–The use of namespaces and examples

namespace Test //Test is the name of the namespace
{
    //The content in the namespace can define variables, functions, and types 
    int  sqrt = 0 ; //Your own namespace, which can conflict with the function name of the library function 
    void  fun ()
     {
         return ;
    }
    struct stu
    {
        int b;
        char c;
    };
}

The following is the use of the namespace (the corresponding header file has been included by default)

Use the format:

namespace name :: variable name or function name in namespace //:: is a scope qualifier

Example of use:

int main()
{
    printf("%d", Test::sqrt);
    return 0;
}

At this point, the print result is 0.

πŸ’–Notes

  1. When the namespace is not specified, the global namespace is used, which is the namespace in the library function we refer to. For example, in the above example, printing sqrt is the address of the sqrt function in the printed library function. %d form.

  2. The same variable name or type name or function name is not allowed in ordinary namespaces, because they are in the same domain and the pointers are ambiguous when used.
    The specific details are as follows:

    1. The variable name and function name cannot be the same.

    2. The variable name or function name can be the same as the type name (structure type).
      When the variable name or function name is the same as the type name, the programmer must bear the corresponding distinction obligation , such as

      namespace Test
      {
      int sqrt = 0;
      void fun()
      {
      return;
      }
      struct sqrt
      {
      int b;
      char c;
      };
      }
      int main()
      {
      Test::sqrt d;
      return 0;
      }

      To write it in the above way, the compiler will Test::sqrtfirst treat it as a variable, not the structure we think. This is the obligation we need to undertake. If we want to treat it as a structure, we must precede it It is enough to add a struct, and let the compiler regard it as a structure. If sqrt is the function name and the structure type name at the same time, the same is true. This place needs great attention.

  3. When using the type name in the namespace, if struct is to be added, it can only be added in front of the namespace name . As follows:

    struct Test : : sqrt d; //correct way
    Test:: sqrt struct d ; //wrong way
    Test:: struct sqrt d ; //wrong way

  4. Local variables will mask global variables. If we want to use global variables directly without using local variables, we can write as follows:

    int a = 0;
    int main()
    {
    int a = 1;
    printf("%d", ::a);
    return 0;
    }

    In this way, adding a :: in front of a will use the global variable a without following the principle of local first.
    Analysis of the reason: The domain scope qualifier is blank, and the default domain is accessed at this time, and the default domain is the global domain.

  5. Multiple namespaces can be defined, and the same variable name or function name can exist in different namespaces.
    For example the following definitions are legal:

    namespace Test
    {
    int a = 0;
    void fun
    {
    return 0;
    }
    }
    namespace Test2
    {
    int a = 0;
    void fun
    {
    return 0;
    }
    }

  6. Although the namespace has an independent domain, we cannot perform the address operation on the namespace, which is illegal.

  7. The variables in the namespace are stored in the global static area. Like global variables, the space has been opened up before the program runs.

  8. The type in the namespace needs to add its namespace name when defining the variable in the main function, but it is not necessary to add the domain name in front of the variable name when using it later . E.g:

    namespace stu
    {
    struct class
    {
    int a;
    char c;
    }
    }
    int main()
    {
    stu::class student = {1,'a'};
    printf("%d\n",student.a);
    }

  9. The same type name can be defined in different namespaces, but a variable with the same name cannot be defined for the same or different type names. At this point namespaces isolate types, not variables . For example, the following operations are illegal:

    namespace Test
    {
    struct stu
    {
    int a;
    char c;
    }
    }
    namespace Test2
    {
    struct stu
    {
    int a;
    char c;
    }
    }
    //The definitions of the above two namespaces are legal
    int main ()
    {
    Test::stu student1 = {1,'a'};
    Test2::stu student1 = {2,'b'};
    return 0;
    }

    In this case, the phenomenon of student1 redefinition occurs, why? Because variable names essentially identify addresses, you cannot define two variables at the same address.

  10. The variables defined in the namespace are essentially global variables, and the life cycle is similar to that of global variables .

  11. Namespaces can only be defined where global variables are defined, not inside functions. In a project, namespaces are generally defined in the .h header file . For example the following definition form:

    int main()
    {
    namespace stu
    {
    int a = 0;
    }
    return 0;
    }

  12. Header files should not contain using declarations.

    This is because the content of the header file is copied to all files that reference it. If there is a using statement in the header file, then every file that uses the header file will have this statement. For some programs, because some names are included inadvertently, it may produce unexpected name conflicts, so the general using statement is generally used in .cpp files.

πŸ’˜3.1.2 Nested definitions of namespaces

Nested definitions of namespaces are used to prevent conflicting variable, function, or type names within the same namespace.

πŸ’– Definition and use of nested namespaces

definition:

namespace school
{
    namespace class
    {
        int a = 0;
    }
}

use:

int main()
{
    printf("%d",school::class::a);
}

πŸ’–Notes

  1. Namespaces with the same name can exist at the same time, and the compiler will merge them when compiling . For example the following are equivalent:

    namespace stu
    {
    int a = 0;
    }
    namespace stu
    {
    int b = 0;
    }
    //The above two namespace definitions are the same as the following
    namespace stu
    {
    int a = 0;
    int b = 0;
    }

  2. The variable or function name inside the namespace defined by nesting can be the same as the external variable or function name, which is also the problem it needs to solve . For example, the following operations are legal:

    namespace stu
    {
    int a = 0;
    void fun()
    {
    return;
    }
    struct n
    {
    int c;
    char d;
    };
    namespace N1
    {
    int a = 1 ; //use :stu::N1::a
    void fun () //use :stu::N1::fun()
    {
    return ;
    }
    struct n // use: stu::N1::n
    {
    int m;
    char n;
    };
    }
    }

πŸ’˜3.1.3 Introduction of namespace

πŸ’– Usage of namespace introduction

Format:

using  namespace namespace name;

Example of use:

Assuming the above namespace definition already appears in another .h header file, then we can import it in the .c file like this:

using  namespace  stu ; //Introduce stu namespace 
using  namespace  N1 ; //Introduce namespace N1

After the namespace is introduced, we can directly use the variables, functions and types defined in stu, but there are many things to pay attention to:

πŸ’–Notes

  1. When we introduce the stu namespace as in the above example, if we use a directly, we will use 0 instead of 1. If we want to use a in N1, we must use it like this: stu::N1::a.

  2. If we expand the above stu and N1 at the same time, the variable a cannot be used directly at this time , because the pointer is not clear, the compiler does not know whether it is in stu or N1. If you want to use a at this time, you must be clear The preceding fields, such as stu::aand stu::N1::a.

  3. The introduction of namespaces is also sequential . If we want to introduce N1, we have two ways to write it

    1. using namespace stu ;
      using namespace N1 ; //The previous line of code must be present at this time, otherwise N1 will not be found

    2. using namespace stu::N1;

  4. What can be imported, only variables or functions or types of a certain namespace can be imported . E.g:

    using stu::a;
    using stu::fun();
    using stu::n;

πŸ’–3.2 Use of namespaces

namespace N
{
    int a = 10;
    int b = 20;
    int Add(int left, int right)
    {
        return left + right;
    }
    int Sub(int left, int right)
    {
        return left - right;
    }
}
int  main ()
 {
     printf ( "%d\n" , a); // This statement compiles incorrectly and cannot recognize a 
    return  0 ;
}

Three ways to use:

  • Add namespace name and scope qualifier

    int main()
    {
    printf("%d\n", N::a);
    return 0;
    }

  • Using using to introduce members in the namespace

    using N::b;
    int main()
    {
    printf("%d\n", N::a);
    printf("%d\n", b);
    return 0;
    }

    Notice:

    We generally use this method:

    using std::cout;

    In this way, some commonly used ones are introduced, because directly introducing a namespace will cause naming pollution, which is prone to redefinition.

  • Import using using namespace namespace name

    using namespce N;
    int main()
    {
    printf("%d\n", N::a);
    printf("%d\n", b);
    Add(10, 20);
    return 0;
    }

πŸ’–3.3 Interpretation of std

==std is the namespace that encapsulates the C++ library. == Such as cout and cin are sealed in the standard namespace.

If we don’t import, then we can only use it like this:

int main()
{
    int a = 0;
    std::cout << a << std::endl;
}

Prepend std:: to the name encapsulated into the standard namespace.

🧑3. C++ Input & Output

Let’s look at a program:

#include<iostream>
using namespace std;
int main()
{
    int a = 0;
    cin >> a;
    cout << a;
    return 0;
}

operation result:

illustrate:

  1. When using cout standard output (console) and cin standard input (keyboard) , the header file and std standard namespace *must be included.
    Note: The early standard library implemented all functions in the global domain and declared it in the header file with the .h suffix. When using it, you only need to include the corresponding header file. Later, it will be implemented in the std namespace, in order to distinguish it from the C header file. , and in order to use the namespace correctly, it is stipulated that the C++ header file does not have .h; the format is also supported in the old compiler (vc 6.0), and the subsequent compilers no longer support it, so the method of +std is recommended
    .*

  2. It is more convenient to use C++ for input and output, without adding data format control, the compiler can automatically identify the type, such as: integer -%d, character -%c

    </p> <h1>include <iostream></h1> <p>using namespace std; int main() { int a; double b; char c;</p> <pre><code>cin >> a; //Enter one data at a time cin >> b >> c; //You can enter multiple data at a time, and the default is to separate them with spaces or newlines cout << a << endl ; // output the value stored in variable a and endl (newline) cout << b << " " << c << endl ; // output the value of variable b and spaces and variable c The value also has a newline character, indicating that multiple data can be output at one time return 0 ; </code></pre> <p>}

    The difference from C language is:

    Whether it is input or output, we do not need to specify the corresponding type, the compiler will automatically perform type recognition and conversion.

  3. In C++, >> is the stream extraction operator and << is the stream insertion operator.

    cin >> a; // Input data from cin (keyboard), and then the data is extracted into variable a, which is the input of variables in C++
    cout << a << endl ; // Insert a into the standard output console ( usually the monitor)

🧑4. Default parameters

Parameters of functions in C++ can also be equipped with tires.

πŸ’–4.1 Default parameter concept

A default parameter is when a function is declared or defined to specify a default value for a function’s parameters. When calling the function, the default value is used if no arguments are specified, otherwise the specified arguments are used.

void TestFunc(int a = 0)
{
    cout << a << endl;
}
int main()
{
    TestFunc(); // When no parameter is passed, the default value of the parameter is used, and the output result is 0 
    TestFunc( 10 ); // When the parameter is passed, the specified actual parameter is used, and the output result is 10 
}

πŸ’–4.2 Default parameter classification

  • full default parameters

    void TestFunc(int a = 10, int b = 20, int c = 30)
    {
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
    }
    int main()
    {
    TestFunc(); //The output result is 10 20 30
    TestFunc( 5 ); //The output result is 5 20 30
    TestFunc( 5 , 6 ); //The output result is 5 6 30
    TestFunc( 5 , 6 , 7 ); / / The output result is 5 6 7
    return 0 ;
    }

    Note: The following syntax is not supported in C++:

    TestFunc(,5,6);

  • Semi-default parameters (pass at least one when using the function)

    void TestFunc(int a, int b = 10, int c = 20)
    {
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
    }

Notice:

  1. Semi-default parameters must be given in order from right to left, and cannot be given at intervals.

  2. Default parameters cannot appear in both function declaration and definition (even if the value is the same, the compiler will displayRedefine default parametersοΌ‰

    //ah
    void TestFunc ( int a = 10 ) ;
    // ac
    void TestFunc ( int a = 20 )
    {}
    // Note: If the declaration and the definition occur at the same time, and the two places happen to provide different values, the compiler will Not sure which default to use.

    Note: At this point, two situations will occur:

    1. Declare to default parameters, define not to default parameters. As follows:

    void fun(int a = 20); void fun(int a) { cout << a << endl; }</p> <p>int main() { fun(); //The output result is 20 fun( 10 ); //The output result is 10 return 0 ; }

    1. The declaration does not give default parameters, but defines default parameters. As follows:

    void fun(int a);
    void fun(int a = 20)
    {
    cout << a << endl;
    }
    int main()
    {
    fun(); //The program cannot run normally, the function does not accept 0 parameters
    //Reason: Before linking, each cpp file will generate a .obj file. If the declaration is not given, .h will be expanded in the source file, When the program is compiled, the program cannot find its default parameters. The program can only find the address of the corresponding function when it is linked, so as to know its default parameters, that is, only the declaration can be obtained during the compilation phase. The default parameter in the definition
    fun( 10 ); //The output result is 10
    return 0 ;
    }

  3. The default value must be a constant or a global variable

  4. C language not supported (compiler not supported)

🧑5. Function overloading

πŸ’–5.1 Function overloading concept

Function overloading : It is a special case of functions. C++ allows to declare several functions of the same name with similar functions in the same scope .different types of parameters)) must be different, and is often used to deal with different data types that implement similar functions.

The meaning of function overloading:

The name of a function just lets the compiler know which function it calls, and function overloading can reduce the burden on programmers to name and remember names to a certain extent.

Note: the main function cannot be overloaded!

Note: C language does not support functions with the same name, as long as the names are the same, it is a redefinition.

{
    return left + right;
}
{
    return left + right;
}
{
    return a + b + c;
}
{
    cout << a << endl;
    cout << c << endl;
}
{
    cout << a << endl;
    cout << c << endl;    
}
int main()
{
    Add(10, 20);
    Add(10.0, 20.0);
    Add(10, 20, 30);
    return 0;
}

(1) Function 1 and function 2 belong to different parameter types

(2) Function 1 and Function 3 have different numbers of parameters

(3) Function 4 and function 5 belong to different parameter order

Q: Why can’t the return value be used as a condition for function overloading?

Answer: We only use the function name and actual parameters when calling a function, and do not involve the return value, so different return values ​​cannot be used as a condition for function overloading.

πŸ’–5.2 Name Modification

Why does C++ support function overloading, but C language does not support function overloading?

In C/C++, to run a program, it needs to go through the following stages: preprocessing, compilation, assembly, and linking .

The reason why C++ supports overloading and C language does not is because of their linking, as shown in the following example: (under linux)

(1) In the case of C++:

There are three source files below

f.cpp

#include"f.h"
void f(int a, double b)
{
    printf("%d %lf", a, b);
}
void f(double a, int b)
{
    printf("%lf %d", a, b);
}

f.h

#include<stdio.h>
void f(int a,double b);
void f(double a, int b);

test.cpp

#include"f.h"
int main()
{
    f(1,3.14);
    f(3.14,1);
    return 0;
}

When assembling, a symbol table (.obj file) will be generated (the mapping formed by the function name and the function address)

Symbol table generated by f.cpp:

function symbol identifier address
_Z1fid 0xffffff11
_Z1fdi 0xffffff22

Symbol table generated by test.cpp:

function symbol identifier address
main function (main function) 0x11223344
_Z1fid ?
_Z1fdi ?

When linking, the symbol table formed by test.cpp will be linked with the symbol table formed by f.cpp. After the link, the two? will be successfully populated, so you can successfully link at this time.

Note: The i above indicates that the first parameter type is int, d indicates that the second parameter type is double, and 1 indicates that the function name has only one character, that is, f.

(2) In case C:

There are three source files below

f.c

#include"f.h"
void f(int a, double b)
{
    printf("%d %lf", a, b);
}
void f(double a, int b)
{
    printf("%lf %d", a, b);
}

f.h

#include<stdio.h>
void f(int a,double b);
void f(double a, int b);

test.c

#include"f.h"
int main()
{
    f(1,3.14);
    f(3.14,1);
    return 0;
}

A symbol table (a mapping of function names and function addresses) is generated during assembly.

Symbol table generated by fc:

function symbol identifier address
f 0xffffff11
f 0xffffff22

Symbol table generated by test.c:

function symbol identifier address
main function (main function) 0x11223344
f ?
f ?

When linking, the symbol table formed by test.c will be linked with the symbol table formed by fc. At this time, the two f with the same name cannot be linked normally, which is why the C language does not support function overloading.

Of course, there are more than these when linking. For example, there are static library/dynamic library calls during the linking process. At this time, we discuss the following issues:

C language can call C language static library/dynamic library, C++ can call C++ static library/dynamic library, then C language can call C++ static library/dynamic library? Can C++ call the static library/dynamic library of C language? The next section is!

πŸ’–5.3 extern β€œC”

Sometimes in a C++ project, some functions may need to be compiled according to the C style, and extern “C” is added before the function, which means to tell the compiler to compile the function according to the C language rules. For example: tcmalloc is a project implemented by google in C++. It provides two interfaces, tcmallc() and tcfree, for use, but if it is a C project, it cannot be used, so he uses extern “C” to solve it.

The following will take you step by step to simulate the implementation in VS2019:

First create a project, as usual, call it AddC.lib here, then create the Add.c source file and the Add.h header file. Created as shown below:

At this point we are ready to generate the static library:

Right-click the project and click Properties.

Change the configuration type, change Application to Static Library, and click OK.

Click Generate Solution at this point.

At this point you can see that the .lib file has been generated.

Go to the project file directory and click on the Debug folder.

At this point, you can see the corresponding generated .lib file.

At this point we have a C++ project and we want to use the AddC.lib static library above.

First, include the header file of the static library just now in the new project. Here is the way of #include path. Of course, we can also directly copy the .h header file to the folder of the current project and then add it directly to the left In the header file option, and then use the #include directive to include it directly in our current project source file.

== Note: At this time, we are still unable to run, and can pass normally when compiling, but there will be problems when linking, because there is a problem when the program is linked, and the corresponding Add function cannot be found. == At this point we still need to configure.

Right-click the project and click to enter the Properties option. Click Linker in the table of contents on the left, click General. Then click Additional Directories, click the down arrow, and click Edit.

First tap the yellow folder icon, then the three dots sign.

Next will build the static library project’s Debug folder selection.

After selecting, click OK.

Click Input on the left, add AddC.lib in the Additional Dependencies option, separate it with a semicolon and other files, and click OK.

At this point, we have successfully configured and run, and found that it cannot run normally, and a link error will appear, as shown in the following figure:

At this time, we changed the static library Add.c file to Add.cpp file and found that the program can run normally and output 2. Why does this happen?

Because our .c file is different from the cpp file when generating the symbol table, the .c file will not have the type of function parameters, but the .cpp file will have the type of function parameters, and our test project is .cpp file, if the static library file is a .c file, of course, it is not possible. At this time, it belongs to the static library called .c by .cpp, and after we change the suffix, it is the static library called .cpp by the .cpp project, which is normal at this time. This is why the above situation arises.

So, if our .cpp file wants to call the static library of .c, how can we do it?

We can add one in front of #include in the Test.cpp file extern ”Cβ€œto tell the C++ compiler: the function declared in this header file is a C library, and it needs to be linked and called in a C way.

The code is modified as shown below:

Note: only extern “C”, no extern “C++”.

After the above modification, even if the file in the AddC static library is Add.c, it can be called normally and output the correct result 2.

Of course, the above configuration tearing is too much trouble, we want to make it simpler, okay?

The answer is yes. In the above example, we can directly copy and paste the AddC.lib static library we generated into the directory of our Test project. The specific operations are as follows:

First find the AddC.lib file, right-click to copy.

Paste it in the directory of the Test project.

After pasting in the past, we found that after we do this, we can also use it directly, and we don’t need to configure the previous one. Similarly, we can also directly copy and paste the Add.h file into the directory of the Test project. Then we don’t need to add the path when including the header file. All in all, both methods have their own advantages and disadvantages.

Q: Can C call the static library of C++?

Answer: Yes. In the above example, we only need to make this modification in Add.h in the static library project:

Then modify our Test.c file into the form of C language:

Then run, you can output 2, indicating that it can run normally at this time.

Or use the following modification:

There are two uses for extern “C”:

  1. Add it directly before the function declaration

extern "C" void f1(int a,int b);
extern "C" void f2(int a,int b);

  1. Enclose the function declaration in curly braces, then add

extern "C"
{
void f1(int a,int b);
void f2(int a,int b);
}

Note: When C calls the static library of C++, it cannot use the static library with function overloading, and it cannot be passed when compiling. The function redefinition is found when the .h header file is expanded. If you still want to call, you must modify the name of the function in the C++ library so that it is no longer overloaded. But this loses the meaning of overloading.

Leave a Comment

Your email address will not be published. Required fields are marked *