Classes and Objects One

Process-oriented and object-oriented difference

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

class definition

  • C++ is compatible with C's structure syntax, and at the same time, the structure is upgraded to a class. The class can not only define variables, but also define functions.
class className{
    // Class body: consists of member functions and member variables
}; 
  • class is the keyword for defining a class, ClassName is the name of the class, and **{}** is the body of the class. Note the semicolon at the end of the class definition.
  • Elements in a class are called members of a class: data in a class is called attributes or member variables of the class; functions in a class are called methods or member functions of the class
// C language definition structure
typedef struct ListNode_C {
    // Cannot use LTNode
    struct ListNode_C* next;
    int val;
}LTNode;
// C++ class definition
struct ListNode_CPP {
    // class name is type
    ListNode_CPP* next;
    int val;
};

Two ways to define a class

  1. All declarations and definitions are placed in the class body. It should be noted that if a member function is defined in a class, the compiler will treat functions that meet the inline conditions (that is, fewer compilation instructions) as inline functions.
class Stack {
public:
	void Init() {
		a = 0;
		top = capacity = 0;
	}
	void Push(int x) {
		// ...
	}
	void Pop() {
		// ...
	}
private:
	int* a;
	int top;
	int capacity;
};
  1. The declaration is placed in the .h file, and the class definition is placed in the .cpp file

Queue.h

struct QueueNode {
	QueueNode* next;
	int val;
};
class Queue {
public:
	void Init();
	void Push(int x);
	void Pop();
private:
	QueueNode* head;
	QueueNode* tail;
};

Queue.cpp

void Queue::Init(){
    // Implementation
}
void Queue::Push(int x) {
    // Implementation
}
void Queue::Pop() {
    // Implementation
}
  • The rules for the definition of a class
  1. Small functions, if you want to be inline, can be defined directly in the class
  2. Large functions should be declared and defined clearly

For example, the specific implementation of void Queue::Init() is relatively simple, and the above code can be changed to

class Queue {
public:
	//void Init();
	inline void Init() {
		head = tail = nullptr;
	}
	void Push(int x);
	void Pop();
private:
	QueueNode* head;
	QueueNode* tail;
};

Class access qualifiers and encapsulation

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

Common Access Qualifier Descriptions

  1. Publicly modified members can be accessed directly outside the class, and can also be accessed inside the class
  2. Protected and private modified members cannot be directly accessed outside the class, but only within the class (protected and private are similar here)
  3. The scope of access rights begins at the occurrence of this access qualifier until the occurrence of the next access qualifier
  4. The default access permission of class is private, and struct is public (because struct is compatible with C)

package

  • The three major characteristics of object-oriented: encapsulation, inheritance, polymorphism.
  • Encapsulation: Organically combine data and methods of operating data, hide the attributes and implementation details of objects through access rights, and only expose interfaces to interact with objects.
  • Encapsulation is essentially a kind of management: it is more convenient for users to use the class; use protected/private to encapsulate members, and open some common member functions for reasonable access to members. So the essence of encapsulation is a management

C -> There is no way to encapsulate, you need to standardize the use of functions to access data, or you can directly access data
C++ -> encapsulation, you must use functions to access data, you cannot access data directly

class scope

  • A class defines a new scope, and all members of the class are in the scope of the class. To define members outside the class body, you need to use the :: scope resolver to indicate which class scope the member belongs to
void Queue::Init(){
    // Implementation
}
void Queue::Push(int x) {
    // Implementation
}
void Queue::Pop() {
    // Implementation
}

instantiation of the class

  • Definition: The process of creating an object with a class type
  • A class describes an object. It is like a model. It defines which members of the class. Defining a class does not allocate actual memory space to store it.
  • A class can instantiate multiple objects, and the instantiated objects occupy the actual physical space and store class member variables

class object model

object storage design

  • Three common ways are as follows
  1. The object contains the various members of the class
  2. Only one copy of the code is saved, and the address where the code is stored is stored in the object
  3. Only save member variables, member functions are stored in the public code segment
class A{
public:
	void PrintA(){
		cout << _a << endl;
	}
	void Func() {
		cout << "Func()" << endl;
	}
private:
	char _a;
};
int main() {
	A aa1;
	A aa2;
	aa1._a = 1;
	aa2._a = 2;
	aa1.PrintA();
	aa2.PrintA();
	return 0;
}

In the disassembly, you can view aa1.PrintA();aa2.PrintA(); the addresses of the functions called are the same

  • Each object member variable instantiated is an independent space and a different variable, but each object calls the PrintA member function is the same

  • When compiling and linking, go to the public code area to find the address of the function according to the function name

  • To sum up, the design of the class object storage method is to save only member variables, and member functions are stored in the public code segment

int main() {
	A* ptr = nullptr;
	ptr->Func();
	return 0;
}
  • It is because of the third storage design that the above code can work properly
  • Although ptr is a null pointer, the member functions of class A are stored in the common code area. When compiling and linking, the function address can be successfully found

class size calculation

// A class has both member variables and member functions
class A1 {
public:
	void f1() {}
private:
	int _a;
};
// There are only member functions in the class
class A2 {
public:
	void f2() {}
};
// There is nothing in the class---empty class
class A3
{};
int main() {
	cout << sizeof(A1) << endl;
	cout << sizeof(A2) << endl;
	cout << sizeof(A3) << endl;
}

  • The size of a class is actually the sum of the "member variables" in the class, of course, pay attention to the memory alignment (the memory alignment is the same as the C structure, you can view: Structure memory alignment details
  • The size of an empty class is 1, and the compiler gives the empty class one byte to uniquely identify objects of this class.

this

First define a date class as follows

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; // year
	int _month; // moon
	int _day; // day
	int a;
};

For us, the Init and Print functions have three parameters and zero parameters, respectively. For the compiler, however, there are four parameters and one parameter, and before each member variable, there is this pointer, as follows, pointing to the identified entity object

class Date{
public:
	void Init(Date* const this, int year, int month, int day){
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print(Date* const this){
		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
	}
private:
	int _year; // year
	int _month; // moon
	int _day; // day
	int a;
};
  • So the C++ compiler adds a hidden pointer parameter this 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), and all "member variables" in the function body All operations 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

this pointer feature

  1. The type of the this pointer: the type of the class * const, that is, in the member function, the this pointer cannot be assigned a value.
  2. can only be used inside a "member function"
  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 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.

common problem

  • Where does the this pointer exist?

Since this is used as a formal parameter, it is generally stored in the stack like the formal parameter; when the this pointer is passed under the vs compiler, it is passed through the ecx register, so that this access variable can improve efficiency

  • Can this pointer be nullable?

This pointer can be null, but conditionally, when the operation we want to perform does not require this pointer to point to an object (such as just printing something), it can be null, for example

class A{
public:
	void Print(){
		cout << "Print()" << endl;
	}
private:
	int _a;
};
int main(){
	A* p = nullptr;
	p->Print();
	return 0;
}

Therefore, calling the Print() function with p will not report an error, because the function is only printing, and there is no statement to point to with the this pointer inside the function. There is no access to a null pointer.

class A{
public:
	void PrintA(){
		cout << _a << endl;
	}
private:
	int _a;
};
int main(){
	A* p = nullptr;
	p->PrintA();
	return 0;
}

For the PrintA() function, the statement that needs to be pointed to by the this pointer: cout<<this -> _a <<endl; will cause the program to crash

Tags: C++

Posted by riddhi on Sat, 17 Sep 2022 21:34:00 +0530