Technology Sharing

C Classes and Objects (I)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Table of contents

Procedure-oriented and object-oriented

Procedural Programming

Object-Oriented Programming

1. Class Definition

Class definition format

Class Domain

2. Class access qualifiers and encapsulation

Access Qualifiers

Encapsulation

3. Instantiation

Instantiation Concept

Object size

4. this pointer

Characteristics of this pointer


Procedure-oriented and object-oriented

  • C language is a typical process-oriented programming language. The design of the program mainly revolves around functions and data structures, emphasizing the function implementation process.
  • C++ supports both procedural and object-oriented programming. The core concepts of object-oriented programming include classes, objects, encapsulation, inheritance, and polymorphism, which make the organization and design of programs more in line with the model of the real world and improve the maintainability and scalability of the code.

Procedural Programming

Procedure-oriented programming is a process-centered programming concept. In procedure-oriented programming, a program is viewed as a collection of functions or procedures that are executed in a certain order to complete a specific task.

advantage:

  • High performance:Because procedural programming directly executes tasks according to the process, it does not require too much object creation and management, so it has higher execution efficiency in some scenarios with high performance requirements, such as low-level system programming and embedded programming. For example, in the operating system kernel, the implementation of process scheduling, memory management and other functions can be better optimized by using procedural programming.
  • Clear logic:For simple program logic, the code is implemented step by step according to the process. The logical structure of the code is clear and easy to understand and maintain. For example, a simple program to calculate the sum of two numbers can be directly defined by using procedural programming.

shortcoming:

  • Poor maintainability:When the program size increases and the functions become more complex, the procedural programming code may become difficult to maintain and expand. Because the coupling between functions is high, modifying one function may affect other related functions.
  • Low code reusability:Code reuse is usually achieved through function calls, but for complex functional modules, reuse is difficult and the functions cannot be well encapsulated and abstracted.

Object-Oriented Programming

Object-oriented programming is a programming concept centered on objects. An object is an entity that contains data (attributes) and methods (behaviors) for operating these data. By encapsulating related data and methods in an object, the integration of data and operations is achieved.

advantage:

  • High maintainability:Encapsulate functions in objects so that the internal implementation of the object is hidden from the outside world, reducing the coupling between modules. When you need to modify a function, you only need to modify the internal implementation of the corresponding object without affecting other unrelated parts. For example, in a graphical interface application, if you want to modify the function of a button, you only need to modify the method corresponding to the button object without affecting other interface elements.
  • Strong code reusability: Through inheritance, polymorphism and other features, code reuse and expansion can be easily achieved. For example, create a base classShape(shape), and then deriveCircle(round),Rectangle(Rectangle) and other subclasses, which can reuse the properties and methods of the base class and perform specific extensions.
  • Good flexibility: Object-oriented programming supports polymorphism, which allows the program to dynamically choose to execute the corresponding method based on the actual type of the object at runtime, increasing the flexibility and scalability of the program.

shortcoming:

  • Performance overhead: Since operations such as object creation and method calling require a certain amount of overhead, the execution efficiency of the program may be affected in some scenarios with extremely high performance requirements.
  • High learning cost:The concepts and features of object-oriented programming are relatively complex, and it is difficult for beginners to learn and understand.

1. Class Definition

Class definition format

• Class is the keyword for defining a class, Data is the name of the class, and {} is the body of the class. Note that the semicolon at 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; the functions in the class are called the methods or member functions of the class.

• In order to distinguish member variables, it is customary to add a special identifier to member variables, such as adding _ or m before or after the member variable. Note that this is not mandatory in C++, it is just a convention.

• In C++, struct can also define classes. C++ is compatible with the usage of struct in C. At the same time, struct has been upgraded to class. The obvious change is that functions can be defined in struct. In general, it is recommended to use class to define classes.

• Member functions defined in a class are inline by default.

  1. class Date
  2. {
  3. public:
  4. void Init(int year, int month, int day)
  5. {
  6. _year = year;
  7. _month = month;
  8. _day = day;
  9. }
  10. private:
  11. // 为了区分成员变量,⼀般习惯上成员变量
  12. // 会加⼀个特殊标识,如_ 或者 m开头
  13. int _year; // year_ m_year
  14. int _month;
  15. int _day;
  16. };
  17. int main()
  18. {
  19. Date d;
  20. d.Init(2024, 3, 31);
  21. return 0;
  22. }

Class Domain

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

• Class scope affects the search rules of compilation. In the following program, if the class scope Stack is not specified for Init, the compiler will treat Init as a global function. When compiling, it will not be able to find the declaration/definition of members such as array, and an error will be reported. Specifying the class scope Stack means that Init is a member function, and members such as array that cannot be found in the current scope will be searched in the class scope.

  1. #include<iostream>
  2. using namespace std;
  3. class Stack
  4. {
  5. public:
  6. // 成员函数
  7. void Init(int n = 4);
  8. private:
  9. // 成员变量
  10. int* array;
  11. size_t capacity;
  12. size_t top;
  13. };
  14. // 声明和定义分离,需要指定类域
  15. void Stack::Init(int n)
  16. {
  17. array = (int*)malloc(sizeof(int) * n);
  18. if (nullptr == array)
  19. {
  20. perror("malloc申请空间失败");
  21. return;
  22. }
  23. capacity = n;
  24. top = 0;
  25. }
  26. int main()
  27. {
  28. Stack st;
  29. st.Init();
  30. return 0;
  31. }

2. Class access qualifiers and encapsulation

Access Qualifiers

• A way to implement encapsulation in C++, using classes to combine the properties and methods of an object to make the object more complete, and selectively provide its interface to external users through access permissions.

• Members modified by public can be directly accessed outside the class; members modified by protected and private cannot be directly accessed outside the class. protected and private are the same, and their difference will be reflected in the inheritance chapter later.

• The scope of the access right starts from the position where the access qualifier appears and ends when the next access qualifier appears. If there is no access qualifier after it, the scope ends at the end of the class.

• Class definition members are private by default when they are not modified by access qualifiers

• Struct is public by default

• Generally, member variables are restricted to private/protected, and member functions that need to be used by others are placed as public.

Encapsulation

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

In the class and object stage, the main focus is on studying the encapsulation characteristics of the class. So what is encapsulation?

Encapsulation: organically combine data and methods of operating data, hide the attributes and implementation details of the object, and only expose interfaces to interact with the object.

Encapsulation is essentially a kind of management, which makes it easier for users to use the class. For example, for a complex device like a computer, the only things provided to users are the power button, keyboard input, display, USB jack, etc., so that users can interact with the computer and complete daily tasks. But in fact, the real working components of the computer are the CPU, graphics card, memory and other hardware components.

Computer users do not need to care about the internal core components, such as how the circuits on the motherboard are laid out, how the CPU is designed, etc. Users only need to know how to turn on the computer and how to interact with the computer through the keyboard and mouse. Therefore, when the computer manufacturer ships the computer, they put a shell on the outside to hide the internal implementation details, and only provide the power switch, mouse and keyboard jacks, etc., so that users can interact with the computer.

To implement encapsulation in C++ language, you can use classes to organically combine data and methods of operating data, use access permissions to hide the internal implementation details of objects, and control which methods can be used directly outside the class.

3. Instantiation

Instantiation Concept

• The process of creating an object in physical memory using a class type is called instantiating an object.

• A class is an abstract description of an object, something like a model, which limits the member variables of the class. These member variables are only declared and no space is allocated. Space is allocated only when objects are instantiated using the class.

• A class can instantiate multiple objects. The instantiated objects occupy actual physical space and store class member variables. To use an analogy: instantiating objects from a class is like using architectural blueprints to build a house in reality. The class is like a blueprint. The blueprint plans how many rooms there are, the size of the rooms, and their functions, but there is no physical building and people cannot live in it. Only when a house is built using a blueprint can people live in it. Similarly, classes are like blueprints and cannot store data. The instantiated objects allocate physical memory to store data.

Object size

Let's analyze which members are in the class object? Each object instantiated from the class has an independent data space, so the object must contain member variables, but does it include member functions? First of all, after the function is compiled, it is a section of instructions, which cannot be stored in the object. These instructions are stored in a separate area (code segment), so if the object must store them, they can only be pointers to member functions. Let's analyze again, is it necessary to store pointers in the object? Date instantiates two objects d1 and d2. d1 and d2 have their own independent member variables _year/_month/_day to store their own data, but the member function Init/Print pointers of d1 and d2 are the same, so it is a waste to store them in the object. If Date is used to instantiate 100 objects, the member function pointers will be stored 100 times, which is a waste. In fact, function pointers do not need to be stored. Function pointers are addresses. Calling functions is compiled into assembly instructions [call address]. In fact, the compiler must find the address of the function when compiling and linking, not at runtime. Only dynamic polymorphism is found at runtime, so the function address needs to be stored.

In the above analysis, we only store member variables in the object. C++ stipulates that objects instantiated by the class must also comply with the memory alignment rules.

Memory alignment rules

The memory alignment rules are exactly the same as those in C language.Reference articles:C language calculation memory alignment

The first member is at offset 0 from the structure.

• Other member variables must be aligned to addresses that are integer multiples of a number (alignment number).

• Note: Alignment number = the smaller value of the compiler's default alignment number and the size of the member.

• The default alignment number in VS is 8

• The total size of the structure is an integer multiple of the maximum alignment number (the minimum of the largest of all variable types and the default alignment parameter).

• If structures are nested, the nested structures are aligned to integer multiples of their own maximum alignment numbers, and the overall size of the structure is an integer multiple of all maximum alignment numbers (including the alignment numbers of nested structures).

No member variable needs to be given 1 byte, because if no byte is given, how can we indicate that the object exists? So 1 byte is given here purely as a placeholder to indicate that the object exists.

4. this pointer

Characteristics of this pointer

The Date class has two member functions, Init and Print. There is no distinction between different objects in the function bodies. So when d1 calls the Init and Print functions, how does the function know whether to access the d1 object or the d2 object? Here we can see that C++ provides an implicit this pointer to solve this problem.

• After the compiler compiles, the member functions of the class will add a pointer of the current class type in the first position of the parameter by default, which is called the this pointer. For example, the real prototype of Init of the Date class is,void Init(Date* const this, int year, int month, int day) • When accessing member variables in a class’s member functions, they are essentially accessed through the this pointer. For example, in the Init function, a value is assigned to _year: this- &gt;_year = year;

• C++ stipulates that the this pointer cannot be explicitly written in the position of actual parameters and formal parameters (the compiler will handle it during compilation), but the this pointer can be explicitly used in the function body.

  1. #include<iostream>
  2. using namespace std;
  3. class Date
  4. {
  5. public:
  6. // void Init(Date* const this, int year, int month, int day)
  7. void Init(int year, int month, int day)
  8. {
  9. // 编译报错:error C2106: “=”: 左操作数必须为左值
  10. // this = nullptr;
  11. // this->_year = year;
  12. _year = year;
  13. this->_month = month;
  14. this->_day = day;
  15. }
  16. void Print()
  17. {
  18. cout << _year << "/" << _month << "/" << _day << endl;
  19. }
  20. private:
  21. // 这⾥只是声明,没有开空间
  22. int _year;
  23. int _month;
  24. int _day;
  25. };
  26. int main()
  27. {
  28. // Date类实例化出对象d1和d2
  29. Date d1;
  30. Date d2;
  31. d1.Init(2024, 7, 1); // d1.Init(&d1, 2024, 7, 1);
  32. d1.Print(); // d1.Print(&d1);
  33. d2.Init(2024, 7, 10); // d2.Init(&d2, 2024, 7, 10);
  34. d2.Print(); // d2.Print(&d2);
  35. return 0;
  36. }