Function and Method

Asked 2 years ago, Updated 2 years ago, 119 views

There is only one function, so the question doesn't arise like that, but there are many questions about the method, so I'm asking you this question.

A method is declared only once, like Function, but in the process of creating an instance, it seems that several objects are created, and different methods are also created for each object.

Simply put, it's the same code after all, rather than dividing the same code into multiple memory regions. Like StackFrame, I don't think it's necessary to have multiple methods if you take Format so that the method can work as a method (you can use keywords like self and this).

In fact, how are the methods implemented in C++, Java, Javascript, and Python? Is it just that each code goes up in the memory area every time an instance is created?

In the case of Python, it just seemed to create one method for each object.

Do static languages like C++ and Java bring together methods and share routines using devices like Stackframe?

programming-language

2022-09-22 08:58

2 Answers

I don't know much about javascript, but it will be similar to other languages.

For the method (non-static member function) you mentioned, there is only one memory with a function code, as is a common function. Methods are not created per instance.

Of course, this is not said to be the case in the standard, but in most implementations, the code of the method has the same location because it is a part of the language implementation that can be processed without allocating the method by instance.

Then, depending on the instance of the class, the method is how it works, which is implemented to allow the function to use the self or this keyword. When a member variable (field) is accessed within the method, the compiler creates the method so that the member variable can be accessed from this without using this.

If you take C++ as an example, you have the following classes:

class Test {
    int a;
public:
    void test() {
        a = 1;
    }
};
int main() {
    Test t;
    t.test();
}

If you look at the x86-64 assembly in test() here:

Test::test():
  push rbp
  mov rbp, rsp
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov DWORD PTR [rax], 1
  nop
  pop rbp
  ret
main:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  lea rax, [rbp-4]
  mov rdi, rax
  call Test::test()
  mov eax, 0
  leave
  ret

In the assembly, the method test() is in a fixed position and is not newly created per instance.

There are the following parts of the contents.

  lea rax, [rbp-4]
  mov rdi, rax
  call Test::test()

Save the address of the instance in rdi before calling test(). In other words, the rdi register has the meaning of this. Next, if you look at the test() internal code, it is shown below.

  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov DWORD PTR [rax], 1

rdiReads the address from the register and stores the value 1 at the address. The first C++ code set 1 for the variable a and the above assembly sets 1 for the address of rdi. Since there is a variable a at the beginning of Test, this will eventually become the address of a.

In other words, in the implementation of C++, the method exists by class, not by instance, and this is passed in the calling phase to access the instance within the method. In C++, the method is usually implemented in such a way that this has the first invisible parameter and is stored there.

Python clearly shows how it will behave when defining methods in the language itself.

class Test:
    def __init__(self):
        self.a = 0
    def test(self):
        self.a = 1

t = Test()
t.test()

When defining a method in Python, self is added as the first parameter. When calling the method, do not put a separate factor for the self parameter as shown above. This is put in by the interpreter at the calling stage.

At the linguistic level, you receive an instance through parameters, and you don't need to create a method for each instance, and so is the implementation.

Think about Java.

class Test {
    private int a;
    public void test() {
        a = 1;
    }
    public static void main() {
        Test t = new Test();
        t.test();
    }
}

The byte code of the above code is as follows.

class Test {
     <ClassVersion=52>
     <SourceFile=OtherClass.java>

     private int a;

     Test() { // <init> //()V
         L1 {
             aload0 // reference to self
             invokespecial java/lang/Object.<init>()V
             return
         }
     }

     public test() { //()V
         L1 {
             aload0 // reference to self
             iconst_1
             putfield Test.a:int
         }
         L2 {
             return
         }
     }

     public static main() { //()V
         L1 {
             new Test
             dup
             invokespecial Test.<init>()V
             astore0
         }
         L2 {
             aload0
             invokevirtual Test.test()V
         }
         L3 {
             return
         }
     }
}

The code calling test() is shown below.

             aload0
             invokevirtual Test.test()V

aload0 places the value of the region variable 0 in the stack. This is the process of entering the call factor for test(), and the region variable 0 will be Test. Invoke Test.test() via invokevirtual.

     public test() { //()V
         L1 {
             aload0 // reference to self
             iconst_1
             putfield Test.a:int
         }
         L2 {
             return
         }
     }

In test(), aload0 is the command to put the local variable 0 in the stack. However, although there is no region variable in the actual Java code, the value accumulated in the stack in invokevirtual becomes the region variable. Although invisible, this is passed through the parameters.

That is, Java also forwards the address of the instance through the stack, not generating the method per instance.

Of course, not all language or language implementations may work this way. However, in most cases, the method exists in one place, and the separation of instances is accessed through invisible parameters.


2022-09-22 08:58

JavaScript has a primitive object When you create an instance with the new keyword, it is connected to the raw object to look at a predefined function. This is called a prototype chain, and it will not be different from the basic aspects of other languages in memory usage of memory.

However, JavaScript is a first-class citizen object and can express high-level functions, so depending on what code you write, a function that goes up to the memory area every time you instantiate the class. (But isn't it the same in other languages?) I don't know this well either.)

JavaScript is not a static language, so this is not interpreted at compile time. this is defined in the execution time. Each instance passes its own context to the prototype method at runtime to confirm this. Looking at this case, it's not much different from the first answer you gave me above.


2022-09-22 08:58

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.