[C++ Essence Shop] 4. C++ classes and objects (on) object-oriented, classes, this pointer

Table of contents

1. Process-oriented and object-oriented

2. Introduction of class

3. Class definition

 4. Class access qualifiers and encapsulation

4.1 Class access qualifiers

4.2 Packaging

 5. Class scope

 6. Class instantiation

7. Class object model

7.1 Storage method of class objects

7.2 Class size

7.2.1 Size of Empty Classes

 7.2.2 Structure memory alignment rules

8. In-depth explanation of this keyword

 8.1 Derivation of this pointer

 8.2 Characteristics of this pointer

 9. The problem of calling a member function by a null pointer


1. Process-oriented and object-oriented

        C language is a process-oriented language, which is oriented to the process of solving problems, and solves problems sequentially through function calls:

For example, washing clothes: take a basin - put water - put clothes - put washing powder...  

        While C++ is based on object-oriented, it focuses on objects. Taking laundry as an example, C++ focuses on: people, clothes, washing powder...  

2. Introduction of class

        In the C language, the structure can only define variables. In C++, the structure is upgraded to a class, in which not only variables can be defined, but also functions can be defined.

struct Date
{
	void addDate(int x) {}
	int _year;
	int _month;
	int _day;
};

      But the above structure is more willing to use class to define in C++.

3. Class definition

        class is the keyword to define the class, the content in {} is the class body of the class, the content defined inside is called the member, the variable in the class is called the attribute of the class or the member variable of the class, ClassName is the name of the class, and The semicolon ";" after the braces must not be omitted.

class ClassName
{
    //类体
};

        Classes can be defined in two ways: 

  1. Declarations and definitions are all placed in the class body (member functions are defined in the class body, and the compiler may treat them as inline functions)
    class Date 
    {
    public:
    	void addDate(int x)
    	{
    		//
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
  2. The class declaration is placed in the .h file, and the member functions are defined in the .cpp file (the class name:: must be added before the member function )
    #pragma once
    class Date
    {
    public:
    	void addDate(int x);
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    
    #include<iostream>
    #include"date.h"
    void Date::addDate(int x)
    {
    	std::cout << "void addDate(int x);";
    }

     4. Class access qualifiers and encapsulation

4.1 Class access qualifiers

        

        In addition, it should be noted that  the default access right of  class  is private , while the default access right of  struct is public (compatible with c language)

4.2 Packaging

        Object-oriented has three characteristics: encapsulation, inheritance, and polymorphism . The other two will be discussed in subsequent articles. The main thing to talk about today is encapsulation. What is encapsulation: organically combine data and methods of manipulating data, and hide object attributes and implementation details. Only expose the interface to interact with the object.

        Encapsulation is essentially a kind of management of data, just like the bank does not open the details of the bank's internal processing funds to us, but only opens one or more windows for us to communicate with our users, which reduces our use costs. The same goes for packaging.

 5. Class scope

        The class redefines a new scope for us. All members of the class are in the scope of the class. To define members outside the class, you need to use the "::" scope operator to indicate which class domain the member belongs to.

class Date 
{
public:
	void addDate();
private:
	int _year;
	int _month;
	int _day;
};
void Date::addDate()
{
	cout << "void addDate()";
}

 6. Class instantiation

         The instantiation of a class is the process of using the class to create an object, and the class is equivalent to a drawing, and the instantiation is the process of realizing the drawing. A class can instantiate multiple objects, and the instantiated objects occupy actual physical space and store class member variables.

 

7. Class object model

7.1 Storage method of class objects

        When the class is instantiated, only member variables are saved, and member functions are stored in the public code segment.

         We can also verify the above storage method by getting the size of the object.

class Date 
{
public:
	void addDate()
	{}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	cout << sizeof(d1);
}

 output:

12

        From the code, we can see that the size of the class is only the size of three int type variables, so it can be proved that the storage model is correct. And the size of our class also obeys the memory alignment rules. 

7.2 Class size

7.2.1 Size of Empty Classes

        When we define an empty class and instantiate this class, the size of the instantiated object is not 0, but 1. This byte is used to occupy a place, indicating that the object does exist. The size of a non-empty class needs to be calculated according to the rules of memory alignment.

class MyClass
{};
int main()
{
	MyClass mc;
	cout << sizeof(mc);
}

output:

1

 7.2.2 Structure memory alignment rules

         Both structures and classes in C++ follow the principle of memory alignment. The memory alignment here is exactly the same as the structure memory alignment in C language, so I won't explain too much. 

  1. The first member is 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 = the smaller value between the compiler's default alignment and 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 of all variable types and the smallest default alignment parameter).
  4. If a structure is nested, the nested structure is aligned to an integer multiple of its own maximum alignment, and the overall size of the structure is an integer multiple of all maximum alignments (including the alignment of the nested structure).

8. In-depth explanation of this keyword

 8.1 Derivation of this pointer

        In the description above, we know that member functions are stored in the public code area, so how can our instantiated objects accurately call their own member variables in member functions? This is the this keyword we are talking about, which is actually the this pointer. The this pointer is actually the first implicit parameter of the member function . When the object we instantiate calls this function, it will automatically pass in its own address.

        The code and output we see:

class Date
{
public:
	void print()
	{
		cout << _year << "年" << _month << "月" << _day << "日";
	}
private:
	int _year = 2023;  //缺省值,用于默认构造时成员变量的默认值,会在后续构造函数中讲到
	int _month = 8;    //暂时不用太过注意
	int _day = 6;
};
int main()
{
	Date d1;
	d1.print();
}

Output:
August 6, 2023

        Actual code (processed by the compiler):

 8.2 Characteristics of this pointer

  1. The type of this pointer: type * const, that is, in member functions, the 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.
//相当于下面的代码
class Date
{
public:
	void print(Date* const this)
	{
		cout << this->_year << "年" << this->_month << "月" << this->_day << "日";
	}
private:
	int _year = 2023;  
	int _month = 8;    
	int _day = 6;
};
int main()
{
	Date d1;
	d1.print(&d1);
}

         However, there is another special situation that will cause us to fail to call the print function, that is, the object d1 we pass in is of const type, as follows:

 9. The problem of calling a member function by a null pointer

        Everyone may have doubts here, how can a null pointer call a member function? The answer is that it can be called when there is no dereferencing operation on the member variable in the function. as follows:

        The print function here is saved in the public code area, and there is no null pointer access behavior, so it can be called successfully.

class Date
{
public:
	void print()
	{
		//这里没有在函数体内对成员函数进行操作,
        //而我们的成员函数是统一放在公共代码区的,
        //所以这里也就没有发生任何的空指针行为,所以是可以被调用成功的。
		cout << "void print()" << endl;
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 6;
};
int main()
{
	Date* d1 = nullptr;
	d1->print();
}

 output:

void print()

        The following print function dereferences the null pointer, so the program crashes.

class Date
{
public:
	void print()
	{
		//这里对空指针进行了解引用的操作,
		//引发了空指针的非法访问,所以代码就会直接崩溃
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
		//引发了异常: 读取访问权限冲突。this 是 nullptr。
	}
private:
	int _year = 2023;
	int _month = 8;
	int _day = 6;
};
int main()
{
	Date* d1 = nullptr;
	d1->print();
}

output: no output

Exception: Exception thrown: Read access violation. this is nullptr.

To sum up: We have almost covered the basic knowledge related to classes, and later we will start to explain the six default member functions of classes, which will be a difficult nut to crack, so stay tuned. Code text is not easy, remember the three consecutive Olympics.

Guess you like

Origin blog.csdn.net/qq_64293926/article/details/132135399