The "object" who has been curious for a long time, this is it?


Author | Programming Guide North

Source | Programming Guide (ID: cs_dev)

Don’t get me wrong, what I’m writing about today is not for me, after all, I haven’t yet...

This article is mainly to talk about my simple understanding of "objects" in programming languages .


Process-oriented vs. object-oriented

Why is C called a procedure-oriented (Procedure Oriented) language, while Java and C++ are called object-oriented (Object Oriented)?

I heard an interesting statement before:

In C language, we write code like this:

function_a(yyy);function_b(xxx);

Looking from left to right, the first thing you see is the function, which is Procedure, so it is called Procedure Oriented.

And in languages ​​like Java we usually look like this:

Worker worker = new Woker("小北");worker.touchFish("5分钟");worker.coding("1小时");

The first thing you see is an object, so it is called Object Oriented.

Back to the topic. In the C language, data and functions that manipulate data are separated from each other. You don't know the relationship between data and functions, which is not supported at the language level.

In the C language, programming is to combine a bunch of function-oriented functions, and call these functions in turn.

This is called process-oriented, which is in fact consistent with the way we think about problems. For example, to implement a snake-eating game, the process-oriented design idea is to analyze the problem first:

  1. Start the game

  2. Randomly generate food

  3. Draw the picture

  4. Receive input and change direction

  5. Judge whether you hit walls, food, etc.

  6. ...

The object-oriented idea is:

First, disassemble the entire game into individual entities: snakes, food, obstacles, rule systems, and animation systems.

Secondly, to implement the functions that these entities should have (ie, member functions), and you also need to consider how different entities interact and pass messages. To put it bluntly, it is the calling relationship and parameter passing.

For example, the rule system receives snakes, food, obstacles as parameters, and can determine whether to eat food or hit a wall.

The animation system can receive snakes, food, obstacles, etc. as parameters, and then dynamically display them on the screen.

The advantage of this is: you can use the object-oriented features of encapsulation, inheritance, and polymorphism to design a low-coupling system, making the system more flexible and easier to maintain.

Okay, the above paragraph can probably be seen as an eight-legged essay. If you have written programs in C and Java/C++, you know the difference. Otherwise, I would say that no amount of high cohesion and low coupling is useful.


How are objects implemented?


The object is composed of a bunch of attributes (member variables) and a series of methods (member functions) . Before talking about this, let me add a function pointer.

We all know that functions are not first-class citizens in languages ​​such as C/C++ and Java. First-class citizens mean that they can be assigned values ​​or used as function parameters, return values, etc., just like other integers and string variables. But in dynamic languages ​​such as JS and Python, functions are first-class citizens and can be used as parameters, return values, and so on.

The reason is that everything is an object in the underlying implementation of such languages . Functions, integers, strings, and floating-point numbers are all objects, so functions have the same status of first-class citizens as other basic types.

In C/C++, although functions are second-class citizens, we can implement functions in disguised form through function pointers for variable assignment, function parameters, and return value scenarios.


What is a function pointer?

After we know the declaration of ordinary variables, the compiler will automatically allocate a suitable memory. The same is true for functions. When compiling, a function is compiled and then placed in a block of memory.

(The above statement is actually very inaccurate, because the compiler will not allocate memory, and the compiled code is also placed on the disk in binary form, and will be loaded into the memory only when the program starts running)

If we also store the first address of the function in a pointer variable, we can call the pointed function through this pointer variable. This special pointer that stores the first address of the function is called a function pointer .

For example, there is a function int func(int a);

How do we declare a function pointer that can point to func?

int (*func_p)(int);

It seems a bit strange. In fact, the declaration format of the function pointer variable is the same as the declaration of the function func, except that func is replaced with (*func_p).

Why do we need parentheses? Because if you don't need parentheses, int *func_p(int); is to declare a function that returns a pointer. The parentheses are to avoid this ambiguity .

Let's look at a few more declarations of function pointers:

int (*f1)(int); // 传入int,返回int void (*f2)(char*); //传入char指针,没有返回值 double* (*f3)(int, int); //传递两个整数,返回 double指针

Let's look at the specific use of a function pointer:

# include <stdio.h>
typedef void (*work)() Work; // typedef 定义一种函数指针类型
void xiaobei_work() {printf("小北工作就是写代码");}
void shuaibei_work() {printf("帅北工作就是摸鱼")}
void do_work(Work worker) {  worker();}int main(void){  Work x_work = xiaobei_work;  Work s_work = shuaibei_work;  do_work(x_work);  do_work(s_work);return 0;}

Output:

Xiaobei’s job is to write code

Shuaibei's job is to fish

In fact, here is a bit used for the purpose of using function pointers, but everyone should realize that the biggest advantage of function pointers is to make the function variable .

We can pass functions as parameters to other functions, so we have the prototype of polymorphism. We can pass different functions to achieve different behaviors.

void qsort(void* base, size_t num, size_t width, int(*compare)(const void*,const void*))

This is the declaration of the qsort function in the C standard library. Its last parameter requires a function pointer to be passed in. This function pointer is responsible for comparing two elements.

Because the method of comparing two elements is only known to the caller, we need to tell qsort in the form of a function pointer how to determine the size of the two elements.

Well, the function pointer is briefly introduced here, and then back to the topic, the object.


Object

So how to simply simulate an object in C language?

Of course, it can only rely on structure, and member functions can be implemented through function pointers. We will not consider other things such as access control, inheritance, etc.

struct Animal {char name[20];void (*eat)(struct Animal* this, char *food); // 成员方法 eatint (*work)(struct Animal* this);    // 成员方法 工作};

But neither eat nor work has any concrete implementation, so we can construct an Animal object in an initialization function.

void eat(struct Animal* this, char *food) {    printf("%s 在吃 %s\n", this->name, food);};
void work(struct Animal* this) {    printf("%s 在工作\n", this->name);}struct Animal* Init(const char *name) {struct Animal *animal = (struct Animal *)malloc(sizeof(struct Animal));    strcpy(animal->name, name);    animal->eat = eat;    animal->work = work;return animal;}

Inside the Init function, we have completed the assignment of the "member function" and some initialization work, and have bound specific implementations to the two function pointers, eat and work.

Next we can use this object:

int main() {struct Animal *animal = Init("小狗"); animal->eat(animal, "牛肉"); animal->work(animal);return 0;}

Output:

Puppy eating beef

Puppy at work

Why does the eat method called by animal need to pass animal as a parameter to the eat method? Doesn’t eat know which animal called it?

The answer is really don't know. An object is actually a meaningful area in memory, and each different object has its own memory location .

But their member functions are stored in the code segment, and only one copy exists.

Therefore, the call method of animal->eat(...) is exactly the same as the direct call of eat(...). The meaning of animal's existence is to transform you from process-oriented to object-oriented thinking, and to transform method calls into inter-object Message delivery.

So when calling a member function, we also need to pass in a parameter this to refer to which object is currently calling.

Since the C language does not support object-oriented, we need to manually pass animal as a parameter to the eat and work functions.

If it is in an object-oriented language such as C++, we don't need to manually pass this parameter directly, like the following:

animal->eat(“牛肉”);animal->work();

In fact, this is the compiler helping us to do this. The above two lines of code will become as follows after the compiler:

eat(animal, "牛肉");work(animal);

Then, the compiler will silently add this as a formal parameter of the member function to the parameter list during the compilation phase.

And which object calls the method, that object will be assigned to this as a parameter.

Students who learn Java must also be very familiar with this this. This is basically the same in Java and C++.

In other words, almost all object-oriented languages ​​have a similar mechanism to implicitly pass the calling object to the member function, such as the object definition in Python:

class Stu:def __init__(self, name, age):self.name = nameself.age = age
def displayStu(self):      print "Name : ", self.name,  ", Age: ", self.age

You can see that the first parameter of each member function must be called self. This self is actually the same as this.

Only in this way, when you access a member variable in a member function, the compiler knows which object you are accessing.

Hey, don't be busy. According to this, wouldn't it be true that if I don't access any member variables in the member function, I don't need to pass this this pointer?

Or can you pass a null pointer?

The theory is true, and it is feasible in C++, such as the following code:

class Stu{public:void Hello() {cout << "hello world" << endl;    }private:char *name;int age;float score;};

Since no member variables are used in the Hello function, we can even play like this:

Stu *stu = new Stu;stu->Hello(); // 正常对象,正常调用stu = NULL;stu->Hello() // 虽然 stu 为 NULL,但是依然不会发送运行时错误

It can actually be seen here:

stu->Hello(); 等价于Hello(NULL);

Since no member variable is used inside the Hello function, there is no need to use the this pointer to locate the memory location of the member variable. In this case, it does not matter that the calling object is not NULL.

But if the Hello function accesses member variables, such as:

void Hello() {cout << "Hello " << this->name << endl;}

Here you need to use this to access the name member variable, which will lead to a coredump of the program at runtime, because we have accessed a NULL address, or an address offset from a certain position based on NULL, this space is definitely not accessible .

A classmate asked this question in the group just before:

The explanation of this question is the same as above, but this conclusion cannot be generalized to other languages, such as Java and Python. The virtual machines of these languages ​​generally do some additional checks, such as judging whether the calling object is a null pointer, etc. If yes, it will trigger a null pointer exception.

And C++ is really purely compiled into assembly, as long as it can run through from the assembly level, then there is no problem, so we can use this "wonderful skill".

The purpose of writing this article is to give everyone a specific understanding of "objects". It is best to understand how objects are laid out in memory or in the JVM.

I used to think that the object is quite magical, a bunch of functions, but later I realized, isn’t this just a structure plus the syntactic sugar of a compiler?

更多精彩推荐
☞他被称为印度“ IT 大王”,富可敌国,却精打细算如守财奴
☞谷歌知名前 AI 研究员无辜被裁,CEO:调查!
☞网易首支 AI 生成歌曲《醒来》正式发布;FSF :苹果 OCSP 事故在道德上不可接受;CentOS 8.3 发布|极客头条☞实战|手把手教你用Python爬取存储数据,还能自动在Excel中可视化

☞程序员应如何理解高并发中的协程
☞2020区块链行业回顾与前瞻
点分享点点赞点在看

Guess you like

Origin blog.csdn.net/csdnsevenn/article/details/111188762