[C++] Classes and Objects (Part 1)

Table of contents

First, the introduction of the class

Second, the definition of the class

3. Class access qualifiers and encapsulation

  1. Access qualifier

  2. Encapsulation

Fourth, the scope of the class

5. Instantiation of the class

Six, class object model

  1. Calculate the size of the class object

  2. The storage method of class objects

  3. Structure/class alignment rules

Seven, this pointer

  1. The derivation of this pointer

  2. Characteristics of this pointer


Let me first talk about the initial understanding of C and C++:

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.

First, the introduction of the class

Only variables can be defined in the C language structure . In C++ , not only variables but also functions can be defined in the structure . For example: In the initial stage of data structure,  the stack implemented in C language can only define variables in the structure ; now  it is implemented in C++, and you will find that functions can also be defined in the struct .

Like the code below, this is how C and C++ differ:

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 be replaced by class in C++.

Second, the definition of the class

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 contents of the class body are called members of the class : the variables in the class are called member variables ; the functions in the class are called member functions of the class .

There are two ways to define a class:

  • 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 .

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

Here I prefer to use the second way.

Suggestions for naming rules for member variables :

// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
	void Init(int year)
	{
		// 这里的year到底是成员变量,还是函数形参?
		year = year;
	}
private:
	int year;
};

After reading the above code, we are a bit confused about who is a member variable and who is a function parameter. So with the following form:

class Date
{
public:
	void Init(int year)
	{
		_year = year;
	}
private:
	int _year;
};

3. Class access qualifiers and encapsulation

  1. Access qualifier

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. Public modified members can be directly accessed outside the class;
  2. Protected and private modified members cannot be directly accessed outside the class (here protected and private are similar);
  3. The scope of access rights starts from the occurrence of this access qualifier until the occurrence of the next access qualifier ;
  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:  Access qualifiers are only useful at compile time, when the data is mapped to memory, there is no difference in access qualifiers.

  2. Encapsulation

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

  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.

  Encapsulation in the C++ language can organically combine data and methods of manipulating data through classes, hide internal implementation details of objects through access rights, and control which methods can be used directly outside the class.

Fourth, the scope of the class

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;
}

5. Instantiation of the class

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

  1. A class is a description of an object. It is a model-like thing, which defines which members a class has, and defines a class without allocating actual memory space to store it; Think of it as a class to describe specific student information.
  2. A class can instantiate multiple objects, and the instantiated objects occupy actual physical space and store class member variables;
  3. Instantiating an object from a class is like building a house using architectural design drawings in reality. Objects can actually store data and occupy physical space

Only instantiated objects can actually store data and occupy physical space;

class Person
{
public:
	void PrintInfo();
public:
	char* _name;
	char* _sex;
	char* _age;
};
void Test()
{
	Person man;
	man._name = "xxxx";
	man._sex = "男";
	man._age = 10;
	man.PrintInfo();
}

Six, class object model

  1. Calculate the size of the class object

#include<iostream>
using namespace std;

class Person
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};
void Person::PrintPersonInfo()
{
	cout << _name << " " << _gender << " " << endl << _age << endl;
}

int main()
{
	Person man;
	cout << sizeof(man) << endl;
	return 0;
}

operation result:

  2. The storage method of class objects

#include<iostream>
using namespace std;

//1.类中既有成员变量,又有成员函数
class A1
{
public:
    void f1() {}
private:
    int _a;
};
//2.类中仅有成员函数
class A2
{
public:
    void f2() {}
};
//3.类中仅有成员变量
class A3
{
private:
    int _a;
};
//4.空类
class A4
{};
int main()
{
    cout << "A1: " << sizeof(A1) << " A2: " << sizeof(A2) << " A3: " << sizeof(A3) << " A4: " << sizeof(A4) << endl;
    return 0;
}

 operation result:

  Conclusion: 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.

  Note: the size of the empty class, the empty class is special, the compiler gives the empty class a byte to uniquely identify the object of this class.

  3. Structure/class alignment rules

  The memory alignment rules of classes are the same as those of structures. You can refer to a blog I wrote before.

  Detailed Explanation of Custom Types (Structure, Bit Field)

Seven, this pointer

  1. The derivation 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, 6, 19);
	d2.Init(2023, 6, 19);
	d1.Print();
	d2.Print();
	return 0;
}

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

  C++ solves this problem by introducing the this pointer, that is: the C++ compiler adds a hidden pointer parameter to each "non-static member function", making the pointer point to the current object (the object that calls the function when the function is running), All operations of "member variables" in the function body are accessed through this pointer. It's just that all operations are transparent to the user, that is, the user does not need to pass it, and the compiler completes it automatically.

  2. Characteristics of this pointer

  1. The type of this pointer: class type * const, that is, in member functions, this pointer cannot be assigned a value;
  2. Can only be used inside a "member function";
  3. The this pointer is essentially a formal parameter of a "member function". When an object calls a member function, the object address is passed as an actual parameter to 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 passed by the compiler through the ecx register and does not need to be passed by the user.

Where does this pointer exist?

Answer: this exists as a formal parameter. Generally, it is on the stack, and some compilers will put it in a register.

Supplementary question:

Analyze the following code:

class A
{
public:
    void Print()
    {
        cout << "Print()" << endl;
    }
    void PrintA()
    {
        cout << _a << endl;
    }
private:
    int _a;
};
int main()
{
    A* p = nullptr;
    p->Print();    // 正常运行
    p->PrintA();   // 运行崩溃
    return 0;
}

Why does this happen?

Answer: call the Print() member function, and Print() does not exist in the object pointed to by p, the compiler will not dereference p, and at the same time, Print() does not use the this pointer to access the data pointed to by it. For this reason, the compiler will not report an error, and will run the Print() function normally. The same is true for (*p).PrintfA(), except that this is used inside PrintfA() to access the data of the space it points to. , resulting in the dereferencing of nullptr, the program naturally crashes!


If there are deficiencies in this article, you are welcome to comment below, and I will correct it as soon as possible.

   Old irons, remember to like and pay attention!!!  

Guess you like

Origin blog.csdn.net/m0_63198468/article/details/131284922