[Getting Started with C++] How to transition from C to C++? What exactly did the founder do to C++?


Insert image description here


Preface

This blog has two main purposes:

  1. Supplement the shortcomings of C language grammar, and how C++ optimizes the unreasonable design of C language, such as: scope, IO, functions, pointers, macros, etc.
  2. Lay the foundation for subsequent class and object learning.

1. Namespace

 In C/C++, there are a large number of variables, functions, and classes to be learned later. 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 comes from this.

Example:

#include <stdio.h>
#include <stdlib.h>
int rand = 10;
// C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
int main()
{
    
    
 printf("%d\n", rand);
return 0;
}
// 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”

1.1 Definition of namespace

 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 {} . The members in {}
are the members of the namespace.
Tips: A namespace defines a new scope, and all content in the namespace is limited to this namespace.

// 1. 正常的命名空间定义
namespace N1
{
    
    
 // 命名空间中可以定义变量/函数/类型
	 int rand = 10;
 
	 int Add(int left, int right)//函数
	 {
    
    
		 return left + right;
	 }
 
	 struct Node//结构体类型
	 {
    
    
	 struct Node* next;
	 int val;
	 };
}
//2. 命名空间可以嵌套
namespace N1
{
    
    
	int a;
	int b;
	int Add(int left, int right)
	 {
    
    
	     return left + right;
	 }
	 
	namespace N2//嵌套命名空间
	 {
    
    
 	    int c;
 	    int d;
 	    int Sub(int left, int right)
	    {
    
    
 	        return left - right;
   		}
 	}
}
//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

//Queue.h
namespace N1
{
    
    
	void QueueInit()
	{
    
    
		cout << "QueueInit()" << endl;
	}
}
//Stack.h
namespace N1
{
    
    
	void StackInit()
	{
    
    
		cout << "StackInit()" << endl;
	}
}

//下面两个文件在Cpp文件中展开时,两个相同命名空间会自动合并成一个
Test.cpp
#include "Queue.h"
#include "Stack.h"

1.2 Namespace usage

How to use members in the namespace? For example, the following direct access:

namespace bit
{
    
    
	 // 命名空间中可以定义变量/函数/类型
	 int a = 0;
	 int b = 1;
 	int Add(int left, int right)
	 {
    
    
		 return left + right;
	 }
	 struct Node
	 {
    
    
	 struct Node* next;
	 int val;
	 };
}
	
int main()
{
    
    
	 // 编译报错:error C2065: “a”: 未声明的标识符
	 printf("%d\n", a);
	return 0;
}

The correct namespace has the following three uses:

  1. Add namespace name and scope qualifier
int main()
{
    
    
    printf("%d\n", N::a);//::是作用限定符
    return 0;    
}
  1. Use using to introduce a member of the namespace
using N::b;
int main()
{
    
    
    printf("%d\n", N::a);
    printf("%d\n", b);
    return 0;    
}
  1. Use using namespace namespace name introduction
using namespce N;
int main()
{
    
    
    printf("%d\n", a);
    printf("%d\n", b);
    Add(10, 20);
    return 0;    
}

2. C++ input and output

 Newborn babies will greet this new world in their own unique way. When C++ first came out, it was considered a new thing. So should C++ also say hello to this wonderful world? Let's take a look at how C++ implements greetings.

#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
    
    
	char c,int i,double d;
	//自动识别变量的类型
	cin>>c>>i>>d;//输入c/i/d
	cout<<c<<i<<d<<endl;//输出c,i,d
	cout<<"Hello world!!!"<<endl;//输出字符串
	return 0;
}

illustrate:

  • When using the cout standard output object (console) and cin standard input object (keyboard), you must include the <iostream> header file and use std by namespace usage.
  • << is the stream insertion operator and >> is the stream extraction operator .
  • In fact, cout and cin are objects of ostream and istream types respectively. cout and cin are global stream objects, endl is a special C++ symbol, indicating newline output (, they are all included in the header file. It is more convenient to use C++ input and output, and there is no need to manually input and output like printf/scanf Control grid. C++ input and output can automatically identify variable types .

Note : In the early standard library, all functions were implemented in the global domain and declared in the header file with the .h suffix. When using it, you only need to include the corresponding header file. Later, it was 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 contain .h ; the old compiler (vc 6.0) also supports the <iostream.h> format, but subsequent compilers no longer support it, so it is recommended to use the +std method

2.1 Usage conventions of std namespace

std is the namespace of the C++ standard library. How to expand std and use it more reasonably?

  • In daily practice, it is recommended to use namespace std directly, which is very convenient.
  • When using namespace std is expanded, the standard library is fully exposed. If we define types/objects/functions with the same name as the library, there will be conflicts. This problem rarely occurs in daily practice, but when there is a lot of code and a large scale in project development, it can easily occur. Therefore, it is recommended to use it in project development. When using it like std::cout, specify the namespace + using std::cout to expand commonly used library objects/types.

3. Default parameters

In actual projects, when we need to open up a space, we often encounter such a problem: how big the space should be. If it is set too large, it will waste space; if it is set small, although realloc can be expanded, if the space is large, multiple expansions will not only be troublesome, but also lead to performance degradation. To solve this problem, Patriarch Benjani gave default parameters.

3.1 Definition of default parameters

Default parameters specify a default value for the function's parameters when declaring or defining a function. When calling this function, if no actual parameter is specified, the default value of the formal parameter is used, otherwise the specified actual parameter is used.

void Func(int a = 0)
{
    
    
 	cout<<a<<endl;
}

int main()
{
    
    
	Func();     // 没有传参时,使用参数的默认值0
	Func(10);   // 传参时,使用指定的实参
	return 0;
}

3.2 Default parameter classification

  • All default parameters
void Func(int a = 10, int b = 20, int c = 30)
 {
    
    
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }
  • semi-default parameters
void Func(int a, int b = 10, int c = 20)
 {
    
    
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

Tips:

  1. Semi-default parameters must be given sequentially from right to left, and cannot be given at intervals.
  2. Default parameters cannot appear in both function declaration and definition. (Generally, declarations are given, definitions are not given)
 //a.h
  void Func(int a = 10);
  
  // a.cpp
  void Func(int a = 20)
 {
    
    }
  
  // 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

//正确:
//a.h
  void Func(int a = 10);
  
  // a.cpp
  void Func(int a )
 {
    
    }
  1. The default value must be a constant or global variable.

4. Function overloading

In natural language, a word can have multiple meanings, and people can judge the true meaning of the word through the context, that is, the word is overloaded
.
For example: There used to be a joke that you don’t need to watch or worry about the two state-owned sports. One is table tennis and the other
is men's football. The former is "No one can win!" and the latter is "No one can win!"

4.1 Concept of function overloading

Function overloading : It is a special case of functions. C++ allows several functions of the same name with similar functions to be declared in the same scope. These functions with the same name have different formal parameter lists (number of parameters or types or type order). This is often used to deal with Implement functions similar to the problem of different data types.

#include<iostream>
using namespace std;

// 1、参数类型不同
int Add(int left, int right)
{
    
    
	 cout << "int Add(int left, int right)" << endl;
	 return left + right;
}
double Add(double left, double right)
{
    
    
	 cout << "double Add(double left, double right)" << endl;
	 return left + right;
}
int main()
{
    
    
 	Add(10, 20);
 	Add(10.1, 20.2);
	 return 0;
}
// 2、参数个数不同
void f()
{
	 cout << "f()" << endl;
}
void f(int a)
}
	 cout << "f(int a)" << endl;
}
int main()
{
	 f();
 	f(10);
	 return 0;
}
// 3、参数类型顺序不同
void f(int a, char b)
{
	 cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	 cout << "f(char b, int a)" << endl;
}
int main()
{
	 f(10, 'a');
	 f('a', 10);
	 return 0;
}

4.2 The principle of C++ supporting function overloading – name mangling

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

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

  1. Actual projects are usually composed of multiple header files and multiple source files. When compiling [when the Add function defined in b.cpp is called in the current a.cpp], there is no Add in the ao target file after compilation and before linking. Function address, because Add is defined in b.cpp, so the address of Add is in bo. So what to do?
  2. Therefore, the link stage is dedicated to dealing with this problem. When the linker sees that ao calls Add, but there is no Add address, it will find the Add address in bo's symbol table , and then link them together.
  3. So when linking, when facing the Add function, which name will the linker use to find it? Each compiler here has its own rules for function name modification.
  • The function name modifications of gcc and g++ are given below:

Compiling in C language:
Compiled in C language:
Conclusion: Under Linux, after compiling with gcc, the modification of the function name has not changed .

The result after compilation using C++ compiler:

Insert image description here

Conclusion: Under Linux, after compilation with g++ is completed, the modification of the function name changes, and the compiler adds function parameter type information to the modified name .

From the above we can see that the names of gcc functions remain unchanged after modification. After modification, the function of g++ becomes [_Z+function length+function name+type first character]

  1. From here, we understand that C language cannot support overloading because functions with the same name cannot be distinguished. C++ is
    distinguished by function modification rules. As long as the parameters are different and the modified names are different, overloading is supported.

Tips: If the function names and parameters of two functions are the same, but different return values ​​do not constitute overloading, because the compiler cannot distinguish them when calling
.


5. Quote

5.1 Reference concept

QuoteInstead of defining a new variable,Give an existing variable an alias, the compiler will not open up memory space for reference variables,It shares the same memory space as the variable it references..
Type & reference variable name (object name) = reference entity

void Test()
{
    
    
    int a = 10;
    //引用类型必须和引用实体是同种类型的
    int& ra = a;//<====定义引用类型
    printf("%p\n", &a);
    printf("%p\n", &ra);
}

operation result:
Insert image description here

5.2 Citation characteristics

  1. References must be initialized when defined
  2. A variable can have multiple references
  3. Once a reference refers to one entity, it cannot refer to other entities.
  4. A reference is just an alias for an object. It does not occupy memory space or have its own address. Therefore, the reference itself cannot be destroyed or deleted
int main()
{
    
    
	int a = 10;
	//int& b;  该语句编译时会报错
	int& b = a;//定义时必须初始化
	
	//1个变量可以有多个引用(a被多次引用)
	int& c=a;
	int& d=a;
	
	//引用一旦引用一个实体,再不能引用其他实体
	int x=2;
	//这里c在前面已经引用了一个实体a,不能在引用x
	//所以此处c=x为赋值语句,而不是c引用实体x
	c=x;
	return 0;
}

5.3 Frequently quoted

In C++, constant reference permissions can be panned and reduced, but cannot be enlarged.

int func()
{
    
    
	int a = 0;
	return a;
}
int main()
{
    
    
	const int& ret = func();//const引用后,会延长临时变量的生命周期
	const int a = 10;
	//int& b = a;//权限扩大
	int b = a;//赋值,只有涉及引用时,才会涉及权限问题

	const int& b = a;//平移
	int c = 12;
	const int& d = c;//缩小

	int i = 10;
	double d = i;
	const double d = i;//C/C++中,类型转化会将原值存放到一个临时变量中,而临时变量具有常性
	return 0;
}

5.4 Usage scenarios

  1. Make parameters

Passing references and passing parameters inavailable at any time, and at the same timeImprove efficiency and modify the formal parameters of output parameters to directly affect the actual parameters.

void Swap(int& left, int& right)
{
    
    
   int temp = left;
   left = right;
   right = temp;
}
  1. do return value

Passing by reference returns only ifIt can be used when the object still exists outside the function scope.. Passing the application back can not onlyImprove efficiencyWell enoughModify the returned object

int& Count()
{
    
    
   static int n = 0;
   n++;
   return n;
}

Next, let’s take a look at what the following code outputs? Why?

int& Add(int a, int b)
{
    
    
    int c = a + b;
    return c;
}
int main()
{
    
    
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

Insert image description here
Explanation:
Insert image description here
Note: If the function returns out of the function scope, if the return object is still there (it has not been returned to the system), you can use reference return. If it has been returned to the system, you must use value return.

5.5 Comparison of efficiency of passing by value and passing by reference

 Using a value as a parameter or return value type, during parameter passing and return, the function will not directly pass the actual parameters or return the variable itself
directly , but will pass the actual parameters or return a temporary copy of the variable, so the value is used as The efficiency of parameter or return value types is very low, especially when the parameter or return value type is very large, the efficiency is even lower.

Performance comparison of value and reference as parameter types

#include <iostream>
using namespace std;
#include <time.h>
struct A {
    
     int a[10000]; };
void TestFunc1(A a) {
    
    };
void TestFunc2(A& a) {
    
    };
void TestRefAndValue()
{
    
    
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
    
    
	TestRefAndValue();
	return 0;
}

Insert image description here
Performance comparison of value and reference as return value types

#include <iostream>
using namespace std;
#include <time.h>
struct A {
    
     int a[10000]; };
A a;
// 值返回
A TestFunc1() {
    
     return a; }
// 引用返回
A& TestFunc2() {
    
     return a; }
void TestReturnByRefOrValue()
{
    
    
	// 以值作为函数的返回值类型
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc1();
	size_t end1 = clock();
	// 以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc2();
	size_t end2 = clock();
	// 计算两个函数运算完成之后的时间
	cout << "TestFunc1 time:" << end1 - begin1 << endl;
	cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

int main()
{
    
    
	TestReturnByRefOrValue();
	return 0;
}

Insert image description here

5.6 The difference between references and pointers

In the grammatical concept, a reference is an alias, which has no independent space and shares the same space with the entity it refers to.But in the underlying implementation, there is actually space, because references are implemented in pointer mode.

int main()
{
    
    
	int a = 10;
	int& ra = a;
	ra = 20;
	int* pa = &a;
	*pa = 20;
	return 0;
}

Next, let’s take a look at the compilation of the above code references and pointers: (I don’t know why the drawings are blurry every time I use Microsoft’s own drawing, so just take a look) The differences between
Insert image description here
references and pointers:

  1. A reference conceptually defines an alias for a variable, and a pointer stores a variable address.
  2. References must be initialized when defined, pointers are not required
  3. After a reference refers to an entity during initialization, it can no longer refer to other entities, and a pointer can point to any entity of the same type at any time.
  4. No NULL reference, but NULL pointer
  5. The meaning in sizeof is different: the reference result is the size of the reference type, but the pointer is always the number of bytes occupied by the address space (
    4 bytes on a 32-bit platform)
  6. Reference self-increment means that the referenced entity is increased by 1, and pointer self-increment means that the pointer is offset backward by the size of the type.
  7. There are multi-level pointers but no multi-level references
  8. The way to access entities is different. The pointer needs to be explicitly dereferenced, and the reference compiler handles it by itself.
  9. References are relatively safer to use than pointers

6. Inline functions

First, let's take a look at how to implement a simple addition macro function in C?

//下面给出3种最常见的错误
//#define ADD(x, y) x+y;     --->执行printf("%d",ADD(1,2));语句 ,带分号会报错
//#define ADD(x, y) x+y      --->执行printf("%d",ADD(1,2)*3);语句,已发现和实际结果不同
//#define ADD(x, y) (x+y)	--->执行printf("%d",ADD(1|2,6&3));语句,同样得不到我们想要的结果。因为+的优先级高于|、&

#define ADD(x, y) ((x)+(y))//正确

We found that using macros to define a macro function has many syntax pitfalls and is very error-prone. For this reason, the founder proposed inline functions.

6.1 Concept

IonlineThe modified function is called an inline function.compile timeThe C++ compiler willExpand where inline functions are called, there is no overhead of function calls to establish stack frames, and inline functions improve the efficiency of program operation.

#include <iostream>
using namespace std;
inline int Add(int x, int y)
{
    
    
	return x + y;
}

int main()
{
    
    
	int c = Add(1, 2);
	return 0;
}

Assembly code:
Insert image description here

6.2 Features

  1. Inline is a method of exchanging space for time. If the compiler treats a function as an inline function, it will replace the function call with the function body during the compilation phase. Disadvantage: it may make the target file larger. Advantage: less calling overhead. , improve program operation efficiency.
  2. Inline is just a suggestion for the compiler. Different compilers may have different inline implementation mechanisms. The general suggestion is: make the function smaller (that is, the function is not very long, there is no accurate statement, it depends on the internal implementation of the compiler), not Functions that are recursive and frequently called are inline modified, otherwise the compiler will ignore the inline feature. The picture below shows the suggestions on inline in the fifth edition of "C++prime":
    Insert image description here
  3. Inline does not recommend the separation of declaration and definition, as separation will cause link errors. Because inline is expanded, there is no function address and the link will not be found.

6.3 Interview questions

What are the pros and cons of macros?
Advantages: No strict type checking, more flexible. For small functions that are frequently called, there is no need to create stack frames, improving efficiency.
Disadvantages: many syntax pitfalls, no type safety checks, and inability to debug.


7. auto keyword (C++11)

In the early days of C/C++, the meaning of auto was: variables modified with auto are local variables with automatic memory, but unfortunately no one has ever used it. In C++11, the standards committee gave auto a new meaning:auto is no longer a storage type indicator, but serves as a new type indicator to instruct the compiler. Variables declared by auto must be deduced by the compiler at compile time.

int main()
{
    
    
	int a = 10;
	//b、c的类型编译器会自动匹配
	auto b = a;
	auto c = 'a';

cout << a << endl;
cout << b << endl;
cout << c << endl;
	//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
	return 0;
}

7.1 Auto usage details

  1. Auto is used in combination with pointers and references.
    When using auto to declare pointer types, there is no difference between using auto and auto*, but when using auto to declare reference types, you must add &
int main()
{
    
    
    int x = 10;
    auto a = &x;
    auto* b = &x;
    auto& c = x;

    *a = 20;
    *b = 30;
     c = 40;
    return 0;
}
  1. Define Multiple Variables on the Same Line
    When multiple variables are declared on the same line, these variables must be of the same type, otherwise the compiler will report an error, because the compiler actually only deduces the first type, and then uses the deduced Type defines other variables.
void TestAuto()
{
    
    
    auto a = 1, b = 2; 
    auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

6.3 Scenarios that cannot be deduced by auto

  1. auto cannot be used as a function parameter
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{
    
    }
  1. auto cannot be used directly to declare arrays
void TestAuto()
{
    
    
    int a[] = {
    
    1,2,3};
    auto b[] = {
    
    456};//err
}

8. Range-based for loop (C++11)

For a ranged collection , it is redundant and sometimes error-prone for the programmer to specify the range of the loop. Therefore, range-based for loops were introduced in C++11.The brackets after the for loop are divided into two parts by the colon ":": the first part is the variable in the range used for iteration, and the second part represents the range being iterated

void TestFor()
{
    
    
	int array[] = {
    
     1, 2, 3, 4, 5 }; 
	for(auto e : array)//依次取数组中的数据赋值给e,自带判断结束,自动迭代
    	 cout << e << " ";
   	 cout<<endl;
   	 
	for(auto& e : array)//这里是把数组中的值赋值给e,的改变不会影响数组,所以要传引用
     	e *= 2;
     for(auto e : array)
    	 cout << e << " ";
   	 cout<<endl;
	return 0;
}

Tips: Similar to a normal loop, you can use continue to end this loop, or you can use break to jump out of the entire loop.


9. Pointer null value nullptr (C++11)

In the traditional C header file (stddef.h), you can see the following code:

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

We found that in C++, NULL is directly defined as 0, leaving a big hole.
For this reason, C++11 introduces the keyword nullptr to represent a null pointer.
Tips:

  1. When using nullptr to represent a pointer null value, there is no need to include the header file.
  2. . In C++11, sizeof(nullptr) occupies the same number of bytes as sizeof((void*)0).

Well, this blog ends here, I hope it is helpful to you.
Insert image description here
Insert image description here

Guess you like

Origin blog.csdn.net/Zhenyu_Coder/article/details/133198555