[C++] Class and Object Part 1

Table of contents

Part 1 of Classes and Objects:

                            1. Preliminary understanding of process-oriented and object-oriented

                            2. Introduction of class

                            3. Class definition

                            4. Class access qualifiers and their encapsulation

                            5. Class scope

                            6. Class instantiation

                            7. Calculation of the size of the object of the class

                            8. The this pointer of the class member function


Part 1 of Classes and Objects:

1. Preliminary understanding of process-oriented and object-oriented

The C language is process-oriented, focusing on the process, analyzing the steps to solve the problem, and solving the problem step by step through function calls.

C++ is based on object-oriented, focusing on objects, splitting one thing into different objects, and relying on the interaction between objects to complete.

  

2. Introduction of class

Only variables can be defined in the C language structure. In C++, not only variables can be defined in the structure, but also functions can be defined. For example, the stack implemented in C language can only define variables in the structure. Now it is implemented in C++. It is found that functions can also be defined in struct.

typedef int DataType;
struct Stack
{
     void Init(size_t capacity)
     {
         _array = (DataType*)malloc(sizeof(DataType) * capacity);
         if (nullptr == _array)
         {
             perror("malloc申请空间失败");
             return;
         }
         _capacity = capacity;
         _size = 0;
     }
    void Push(const DataType& data)
    {
         // 扩容
         _array[_size] = data;
         ++_size;
    }
     DataType Top()
     {
         return _array[_size - 1];
     }
     void Destroy()
     {
         if (_array)
         {
             free(_array);
             _array = nullptr;
             _capacity = 0;
             _size = 0;
         }
     }
     DataType* _array;
     size_t _capacity;
     size_t _size;
};
int main()
{
     Stack s;
     s.Init(10);
     s.Push(1);
     s.Push(2);
     s.Push(3);
     cout << s.Top() << endl;
     s.Destroy();
     return 0;
}

The definition of the above structure is preferred to use class instead in C++.

3. Class definition

class className
{
    // 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号

class is the keyword to define the class, className is the name of the class, and {} is the body of the class. Note that the semicolon after the end of the class definition cannot be omitted.

The content in the class body is called the members of the class: the variables in the class are called the attributes or member variables of the class, and the functions in the class are called the methods or member functions of the class.

There are two ways to define a class:

1. The declaration and definition are all placed in the class body. Note: If the member function is defined in the class, the compiler may treat it as an inline function. Therefore, generally small and frequently called functions are placed in the class, and others are placed in the .cpp file to keep the declaration and definition separated.

2. The class declaration is placed in the .h file, and the member function definition is placed in the .cpp file. Note that the member function name needs to be added with the class name::

 In general, the second approach is more desirable. The reason for the separation of declaration and definition: it is convenient for others to read your code, and secondly, when importing a static library, it avoids source code leakage. When the declaration is separated from the definition, the definition should reflect the class domain (void Stack::Init(int N = 4)) and the members in such a class can also be used at will because they specify which class they are.

Suggestions for member function naming rules:

class Date
{
public:
     void Init(int year)
     {
         _year = year;
     }
private:
     int _year;
};
// 或者这样
class Date
{
public:
     void Init(int year)
     {
         mYear = year;
     }
private:
     int mYear;
};
// 其他方式也可以的,主要看公司要求。一般都是加个前缀或者后缀标识区分就行

4. Class access qualifiers and their encapsulation

Access qualifiers:

The way of C++ to achieve encapsulation: use classes to combine the properties and methods of the object to make the object more complete, and selectively provide its interface to external users through access rights.

 Access qualifier description:

1. Members modified by public can be directly accessed outside the class.

2. Protected and private modified members cannot be directly accessed outside the class.

3. The scope of access rights starts from the position where the access qualifier appears until the next access qualifier appears.

4. If there is no access qualifier behind, the scope will go to }, that is, the end of the class.

5. The default access permission of class is private, and struct is public (because struct is compatible with C)

Note: 1. Access qualifiers are only useful at compile time. When data is mapped to memory, there is no difference between access qualifiers.

           2. Access qualifiers restrict access outside the class, not inside the class.

           3. C++ does not require variables or functions to be defined above because the compiler treats the class as a whole.

Interview questions:

Question: What is the difference between struct and class in C++?

Answer: C++ needs to be compatible with C language, so struct in C++ can be used as a structure. In addition, struct in C++ can also be used to define classes. It is the same as class definition class, the difference is that the default access right of class defined by struct is public, and the default access right of class defined by class is private. Note: There is also a difference between struct and class in inheritance and template parameter list positions.

encapsulation

Interview questions:

The three major characteristics of object-oriented: encapsulation, inheritance, and polymorphism.

In the class and object phase, the encapsulation characteristics of the class are mainly studied:

Encapsulation: organically combine the data and the method of manipulating the data, hide the properties and implementation details of the object, and only expose the interface to interact with the object.

Encapsulation is essentially a management that makes it easier for users to use classes. For example: For a complex device such as a computer, the only thing provided to the user is the power button, through keyboard input, monitor, USB jack, etc., allowing the user to interact with the computer and complete daily affairs, etc., but in fact the computer actually works. It is some hardware components such as CPU, graphics card, memory, etc.

  

For computer users, there is no need to care about internal core components, such as how the circuits on the motherboard are laid out, how the CPU is designed inside, etc. Users only need to know how to shut down the computer and how to interact with the computer through the keyboard and mouse. Therefore, when the computer manufacturer leaves the factory, it puts a shell on the outside to hide the internal implementation details, and only provides the switch, mouse, and keyboard jacks to the outside, so that users can interact with the computer.

To achieve encapsulation in C++ language, data and methods of operating data can be organically combined through classes, and internal implementation details of objects can be hidden through access rights, and control methods can be used directly outside the class.

5. Class scope

A class defines a new scope, and all members of the class are in the scope of the class. When defining members outside the class, you need to use the :: scope operator to indicate which class domain the member belongs to.

class Person
{
public:
     void PrintPersonInfo();
private:
     char _name[20];
     char _gender[3];
     int  _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
     cout << _name << " "<< _gender << " " << _age << endl;
}

Note: A class is the same as a structure. The type is defined in it, and it is only declared. It can only be accessed when the object is defined (st.a). Therefore, :: cannot be used to access the members of the class (Stack::a), but it cannot be used to access member functions in the class because no object address is passed to this pointer (Stack::Print).

6. Class instantiation

The process of creating an object from a class is called instantiation of the class

1. A class is a description of an object. It is a model-like thing, which limits the members of a class, but defines a class without allocating actual memory space to store it.

2. A class can instantiate multiple objects, and the instantiated objects occupy actual physical space and store class member variables. The Person class has no space, and only objects instantiated from the Person class have specific space.

int main()
{
     Person._age = 100;   // 编译失败:error C2059: 语法错误:“.”
     return 0;
}
3. To make an analogy, instantiating an object from a class is like building a house using architectural design drawings in reality. A class is like a design drawing. It only designs what is needed, but there is no physical building. The same class is just a Design, instantiated objects can store actual data and occupy physical space.

 

7. Calculation of the size of the object of the class

How to calculate the size of a class object:

class A
{
public:
    void PrintA()
    {
       cout<<_a<<endl;
    }
private:
    char _a;
};

Question: A class can have both member variables and member functions, so what does a class object contain? How to calculate the size of a class?

Guess how the class object is stored:

1. Object storage contains various members of the class:

Defect: The member variables in each object are different, but the same function is called. If stored in this way, when multiple objects are created in one class storage, each object will save a copy of the code, and the same code will be saved multiple times. Waste of space, so how to solve it?
2. Only one copy of the code is saved, and the address where the code is stored is saved in the object

 3. Only member variables are saved, and member functions are stored in public code segments

Conclusion: 1. In practice, for the above three storage methods, the computer stores according to the third storage method.

           2. The size of a class is actually the sum of the member variables in the class. Of course, attention should be paid to memory alignment.

           3. The size of the empty class is 1 byte: it occupies a place, does not store valid data, and indicates that the object exists.

// 类中既有成员变量,又有成员函数
class A1 {
public:
    void f1()
    {}
private:
    int _a;
};
// 类中仅有成员函数
class A2 {
public:
   void f2() 
   {}
};
// 类中什么都没有---空类
class A3
{};

interview questions

1. How to align the structure? Why memory alignment?

2. How to align the structure according to the specified alignment parameters? Can it be aligned according to 3, 4, 5, that is, any number of bytes?

3. What is big and small endian? How to test whether a machine is big-endian or little-endian? Have you ever encountered a scenario where you need to consider big-endian or small-endian?

Structure memory alignment rules:

1. The first member is at the address at offset 0 from the structure.
2. Other member variables should be aligned to an address that is an integer multiple of a certain number (alignment number).
Note: Alignment = Compiler's default alignment and the smaller value of the member size.
The default alignment number in VS is 8
3. The total size of the structure is: an integer multiple of the maximum alignment number (the largest variable type and the default alignment parameter are the smallest).
4. If a structure is nested, the nested structure is aligned to an integer multiple of its own maximum alignment, and the entire structure
The body size is an integer multiple of all maximum alignments (including those of nested structures).

8. The this pointer of the class member function

The introduction of this pointer:

class Date
{ 
public:
     void Init(int year, int month, int day)
     {
     _year = year;
     _month = month;
     _day = day;
     }
     void Print()
     {
         cout <<_year<< "-" <<_month << "-"<< _day <<endl;
     }
private:
     int _year;     // 年
     int _month;    // 月
     int _day;      // 日
};
int main()
{
     Date d1, d2;
     d1.Init(2022,1,11);
     d2.Init(2022, 1, 12);
     d1.Print();
     d2.Print();
     return 0;
}

For the above class, there is such a problem:

There are two member functions Init and Print in the Date class, and there is no distinction between different objects in the function body, so when di calls the Init function, how does the function know that the di object should be set instead of the d2 object?

In C++, this problem is solved by introducing the this pointer, that is: the C++ compiler adds a hidden pointer parameter to each "non-static member function", so that the pointer points to the current object (the object that calls the function when the function is running), All "member variable" operations in the function body are accessed through this pointer, but all operations are transparent to the user, that is, the user does not need to pass it, and the compiler completes it automatically.

Note: The definition and transfer of this pointer are the work of the compiler, we cannot interfere but we can use this pointer in the class.

The characteristics of this pointer:
1. The type of this pointer: the type of the class * const, that is, in member functions, the this pointer cannot be assigned a value
2. Can only be used inside member functions
3. The this pointer is essentially the formal parameter of the member function. When the object calls the member function, the object address is passed as the actual parameter to the this formal parameter. So the this pointer is not stored in the object.
4. The this pointer is the first implicit pointer parameter of the member function. Generally, it is automatically transmitted by the compiler through the ecx register and does not need to be passed by the user.

 Interview questions:

1. Where does this pointer exist?

The this pointer is used as a formal parameter, and generally exists in the stack frame. (VS is optimized below, using the ecx register to pass)

2. Can this pointer be empty?

The this pointer cannot be empty, and an error will be reported when dereferencing.

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
     void Print()
     {
         cout << "Print()" << endl;
     }
private:
     int _a;
};
int main()
{
     A* p = nullptr;
     p->Print();
     return 0;
}
答案:C
//不发生过解引用,因为成员函数的地址不在对象中,在公共代码区域 Print(p) -> void Print(A* const this),传递一个指针不会报错
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:
    void PrintA() 
   {
        cout << _a << endl;
   }
private:
     int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
}
答案:B
//cout<<this->_a 空指针解引用 程序运行崩溃 成员变量在对象里面,要通过this指针去访问

Comparison of Stack in C language and C++:

1. C language implementation:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int DataType;
typedef struct Stack
{
	DataType* array;
	int capacity;
	int size;
}Stack;
void StackInit(Stack* ps)
{
	assert(ps);
	ps->array = (DataType*)malloc(sizeof(DataType) * 3);
	if (NULL == ps->array)
	{
		assert(0);
		return;
	}
		ps->capacity = 3;
	ps->size = 0;
}
void StackDestroy(Stack* ps)
{
	assert(ps);
	if (ps->array)
	{
		free(ps->array);
		ps->array = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}
void CheckCapacity(Stack* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity * 2;
		DataType* temp = (DataType*)realloc(ps->array,
			newcapacity * sizeof(DataType));
		if (temp == NULL)
		{
			perror("realloc申请空间失败!!!");
			return;
		}
		ps->array = temp;
		ps->capacity = newcapacity;
	}
}
void StackPush(Stack* ps, DataType data)
{
	assert(ps);
	CheckCapacity(ps);
	ps->array[ps->size] = data;
	ps->size++;
}
int StackEmpty(Stack* ps)
{
	assert(ps);
	return 0 == ps->size;
}
void StackPop(Stack* ps)
{
	if (StackEmpty(ps))
		return;
	ps->size--;
}
DataType StackTop(Stack* ps)
{
	assert(!StackEmpty(ps));
	return ps->array[ps->size - 1];	
}
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->size;
}

It can be seen that when implemented in C language, Stack-related operation functions have the following commonality:

1. The first parameter of each function is Stack*

2. The first parameter must be detected in the function because the parameter may be NULL

3. In the function, the stack is manipulated through the Stack* parameter

4. The address of the Stack structure variable must be passed when calling

Note: Only the structure for storing data can be defined in the structure, and the method of operating data cannot be placed in the structure, that is, the data and the method of operating data are separated, and the implementation is quite complicated, involving a large number of pointer operations. If you don't pay attention, you may make mistakes.

2. C++ implementation

typedef int DataType;
class Stack
{
public:
	void Init()
	{
		_array = (DataType*)malloc(sizeof(DataType) * 3);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = 3;
		_size = 0;
	}
	void Push(DataType data)
	{
		CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	void Pop()
	{
		if (Empty())
			return;
		_size--;
	}
	DataType Top() { return _array[_size - 1]; }
	int Empty() { return 0 == _size; }
	int Size() { return _size; }
	void Destroy()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	void CheckCapacity()
	{
		if (_size == _capacity)
		{
			int newcapacity = _capacity * 2;
			DataType* temp = (DataType*)realloc(_array, newcapacity *
				sizeof(DataType));
			if (temp == NULL)
			{
				perror("realloc申请空间失败!!!");
				return;
			}
			_array = temp;
			_capacity = newcapacity;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
int main()
{
	Stack s;
	s.Init();
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);

	printf("%d\n", s.Top());
	printf("%d\n", s.Size());
	s.Pop();
	s.Pop();
	printf("%d\n", s.Top());
	printf("%d\n", s.Size());
	s.Destroy();
	return 0;
}

In C++, data and methods of manipulating data can be perfectly combined through classes, and access rights can be used to control which methods can be called outside the class, that is, encapsulation. When using it, it is like using its own members, which is more in line with human beings. Cognition of things, and each method does not need to pass Stack* parameters, the parameters will be automatically restored after the compiler compiles, that is, the Stack* parameters in C++ are maintained by the compiler, and the C language needs to be maintained by the user.

Guess you like

Origin blog.csdn.net/qq_66767938/article/details/129935855