Static binding and dynamic binding. Understanding Static and Dynamic Binding Base Class Object

There are a huge and growing number of systems where extreme levels of static communication can have a huge positive impact on applications and system performance.

I'm talking about what are often called "embedded systems", many of which now increasingly use general purpose operating systems, and these systems are used for everything imaginable.

An extremely common example is devices using GNU/Linux systems using Busybox. I've done this to the extreme with NetBSD by creating a bootable i386 (32-bit) system image that includes both the kernel and its root filesystem, which contains one statically linked (via crunchgen) binary with hard links to all programs , which contain all(well at last count 274) (most of it excluding the toolchain) and that's less than 20 mega bytes (and probably runs very comfortably on a system with 64MB of memory (even with the root filesystem uncompressed and entirely in RAM), although I couldn't find one that small to test).

Previous posts have mentioned that the startup time of static linked binaries is faster (and it can be much faster), but that's only part of the picture, especially when all the object code is linked into the same file, and even more so when the operating system supports swap request code directly from the executable file. In this ideal scenario, program startup times are literally negligible, since almost all pages of code are already in memory and will be used by the shell (and init by any other background processes that may be running), even if the requested program has never been run since boot, since There may only be one page of memory loaded to meet the program's runtime requirements.

However, this is not the whole story. I also typically build and use a NetBSD operating system installation for my complete development systems by statically linking all the binaries. Even though this requires a huge amount of disk space (~6.6GB total for x86_64 with everything including toolchain and X11 static-linked) (especially if you keep full debug symbol tables available to all programs for ~2 more .5 GB), the result is still faster overall, and some tasks even use less memory than a typical dynamically linked system designed to exchange library code pages. Disk is cheap (even a fast disk) and memory for caching frequently used files on disk is also relatively cheap, but CPU cycles really aren't, and paying ld.so's initial earnings for each process that starts every time it starts will take hours and hours of CPU cycles from tasks that require many processes to run, especially when the same programs are used over and over again, such as compilers on a development system. Static bundled software programs can reduce the time needed to create a multicast architecture for an entire system in hours. I have yet to build the toolchain in my single crunchgen"ed binary, but I suspect that when I do there will be more build time saved due to the gain for the CPU cache.


I'm really confused about dynamic binding and static binding. I read that determining the type of an object at compile time is called static binding and determining it at run time is called dynamic binding.

What's happening in the code below:

Static binding or dynamic binding?
What kind of polymorphism is this?

Class Animal ( void eat() ( System.out.println("Animal is eating"); ) ) class Dog extends Animal ( void eat() ( System.out.println("Dog is eating"); ) ) public static void main(String args) ( Animal a=new Animal(); a.eat(); )


2018-05-20 10:33


2018-05-20 10:46

Check this employee class has abstract earning() function and every class has toString() implementation

Employee employees = new Employee; // initialize array with Employees employees = new SalariedEmployee(); employees = new HourlyEmployee(); employees = new CommissionEmployee(); employees = new BasePlusCommissionEmployee(); for (Employee currentEmployee: employees)( System.out.println(currentEmployee); // invokes toString System.out.printf("earned $%,.2f%n", currentEmployee.earnings()); )

All calls to the toString and earnings method are resolved at execution time, based on the type of the object to which the current employee belongs,

This process is known as dynamic binding or late binding

Binding in C++

The two main goals in developing the C++ programming language were memory efficiency and execution speed. It was intended as an improvement to the C language, particularly for object-oriented applications. The basic principle of C++: no language property should lead to additional overhead (both in memory and speed) if this property is not used by the programmer. For example, if all of C++'s object orientation is ignored, then the rest should be as fast as traditional C. So it's not surprising that most methods in C++ are linked statically (at compile time) rather than dynamically (at runtime). .

Method binding in this language is quite complex. For ordinary variables (not pointers or references), this is done statically. But when objects are designated using pointers or references, dynamic binding is used. In the latter case, the decision to choose a method of a static or dynamic type is dictated by whether the corresponding method is declared using the virtual keyword. If it is declared this way, then the message search method is based on a dynamic class; if not, it is based on a static one. Even in cases where dynamic binding is used, the validity of any request is determined by the compiler based on the static class of the recipient.

Consider, for example, the following description of classes and global variables: class Mammal

printf("cant speak");

class Dog: public Mammal

printf("wouf wouf");

printf("wouf wouf, as well");

Mammal *fido = new Dog;

The expression fred.speak() prints "cant speak", but calling fido->speak() will also print "cant speak" because the corresponding method in the Mammal class is not declared virtual. The expression fido->bark() is not allowed by the compiler, even if the dynamic type for fido is Dog. However, the static type of a variable is just the Mammal class.

If we add the word virtual:

virtual void speak()

printf("cant speak");

then we get the expected result at the output for the fido->speak() expression.

A relatively recent change in the C++ language is the addition of facilities for recognizing the dynamic class of an object. They form the RTTI (Run-Time Type Identification) system.

In an RTTI system, each class has an associated structure of type typeinfo, which encodes various information about the class. The name data field, one of the data fields of this structure, contains the class name as a text string. The typeid function can be used to parse data type information. Therefore, the following command will print the string "Dog" the dynamic data type for fido. In this example, it is necessary to dereference the fido pointer variable so that the argument is the value that the pointer refers to, rather than the pointer itself:

cout<< «fido is a» << typeid(*fido).name() << endl;

You can also ask, using the before member function, whether one structure with data type information is a subclass of the class associated with another structure. For example, the following two statements produce true and false:

if (typeid(*fido).before (typeid(fred)))…

if (typeid(fred).before (typeid(lassie)))…

Before the RTTI system, a standard programming trick was to explicitly code into the class hierarchy the methods to be an instance. For example, to test the value of a variable of type Animal to see if it is of type Cat or type Dog, one could define the following system of methods:

virtual int isaDog()

virtual int isaCat()

class Dog: public Mammal

virtual int isaDog()

class Cat: public Mammal

virtual int isaCat()

You can now use the command fido->isaDog() to determine whether the current value of fido is a value of type Dog. If a non-zero value is returned, then the type of the variable can be cast to the desired data type.

By returning a pointer rather than an integer, we combine subclass testing and type casting. This is similar to another part of the RTTI system called dynamic_cast, which we will describe briefly. If a function in the Mammal class returns a pointer to Dog, then the Dog class must be previously declared. The result of the assignment is either a null pointer or a valid reference to the Dog class. So, the result must still be checked, but we eliminate the need for a type cast. This is shown in the following example:

class Dog; // preliminary description

virtual Dog* isaDog()

virtual Cat* isaCat()

class Dog: public Mammal

virtual Dog* isaDog()

class Cat: public Mammal

virtual Cat* isaCat()

Operator lassie = fido->isaDog(); Now we will always do it. As a result, lassie is set to a non-zero value only if fido has the dynamic class Dog. If fido is not owned by Dog, then lassie will be assigned a null pointer.

lassie = fido->isaDog();

... // fido is indeed of type Dog

... // assignment didn't work

... // fido is not of type Dog

Although a programmer can use this method to reverse polymorphism, the disadvantage of this method is that it requires adding methods to both the parent and child classes. If many children derive from one common parent class, the method becomes unwieldy. If changes are not allowed in the parent class, this technique is not possible at all.

Since such problems occur frequently, a general solution was found. The dynamic_cast template function takes a type as a template argument and, just like the function defined above, returns either the value of the argument (if the type cast is legal) or the null value (if the type cast is illegal). An assignment equivalent to the one made in the previous example can be written like this:

// convert only if fido is a dog

lassie = dynamic_cast< Dog* >(fido);

// then check if the cast is successful

Three more cast types have been added to C++ (static_cast, const_cast, and reinterpret_cast), but they are used in special cases and are therefore not described here. Programmers are encouraged to use them as a safer option instead of the old type casting mechanism.

2. Design part

This paragraph, despite its brevity, is very important - almost all professional programming in Java is based on the use of polymorphism. At the same time, this topic is one of the most difficult for students to understand. Therefore, it is recommended to carefully re-read this paragraph several times.

Class methods are marked with the static modifier for a reason - for them, when compiling the program code, static linking. This means that in the context of which class the method name is indicated in the source code, a link is placed to the method of that class in the compiled code. That is, it is carried out method name binding at the place of call with executable code this method. Sometimes static linking is called early binding, since it occurs at the compilation stage of the program. Static binding in Java is used in one more case - when a class is declared with the final modifier (“final”, “final”),

Object methods in Java are dynamic, that is, they are subject to dynamic linking. It occurs at the stage of program execution directly during a method call, and at the stage of writing this method it is not known in advance from which class the call will be made. This is determined by the type of object for which this code works - which class the object belongs to, from which class the method is called. This binding occurs long after the method code has been compiled. Therefore, this type of binding is often called late binding.

Program code based on calling dynamic methods has the property polymorphism– the same code works differently depending on what type of object calls it, but does the same things at the level of abstraction related to the source code of the method.

To explain these words, which are not very clear at first reading, let’s consider the example from the previous paragraph - the work of the moveTo method. Inexperienced programmers think that this method should be overridden in every descendant class. This can actually be done, and everything will work correctly. But such code will be extremely redundant - after all, the implementation of the method will be exactly the same in all derived classes of Figure:

public void moveTo(int x, int y)(

Moreover, this case does not take advantage of polymorphism. So we won't do that.

It is also often puzzling why an implementation of this method should be written in the abstract Figure class. After all, the calls to the hide and show methods used in it, at first glance, should be calls to abstract methods - that is, it seems they cannot work at all!

But the hide and show methods are dynamic, which, as we already know, means that the association of the method name and its executable code is done at the program execution stage. Therefore, the fact that these methods are specified in the context of the Figure class does not mean that they will be called from the Figure class! Moreover, you can guarantee that the hide and show methods will never be called from this class. Let us have variables dot1 of type Dot and circle1 of type Circle, and they are assigned references to objects of the corresponding types. Let's look at how the dot1.moveTo(x1,y1) and circle1.moveTo(x2,y2) calls behave.

When dot1.moveTo(x1,y1) is called, the moveTo method is called from the Figure class. Indeed, this method in the Dot class is not overridden, which means it is inherited from Figure. In the moveTo method, the first statement is a call to the dynamic hide method. The implementation of this method is taken from the class of which the dot1 object that calls this method is an instance. That is, from the Dot class. Thus, the point is hidden. Then the coordinates of the object are changed, after which the dynamic show method is called. The implementation of this method is taken from the class of which the dot1 object that calls this method is an instance. That is, from the Dot class. Thus, a dot is shown at the new location.

For calling circle1.moveTo(x2,y2) everything is absolutely similar - the dynamic methods hide and show are called from the class of which the circle1 object is an instance, that is, from the Circle class. Thus, it is the circle that is hidden in the old place and shown in the new one.

That is, if the object is a point, the point moves. And if the object is a circle, the circle moves. Moreover, if someday someone writes, for example, an Ellipse class that is a descendant of Circle, and creates an Ellipse object ellipse=new Ellipse(...), then calling ellipse.moveTo(...) will move the ellipse to a new location. And this will happen in accordance with the way the hide and show methods are implemented in the Ellipse class. Note that the polymorphic code of the Figure class that was compiled a long time ago will work. Polymorphism is ensured by the fact that references to these methods are not placed in the code of the moveTo method at compile time - they are configured to methods with such names from the class of the calling object immediately at the time the moveTo method is called.

In object-oriented programming languages, two types of dynamic methods are distinguished - actually dynamic and virtual. According to the principle of operation, they are completely similar and differ only in the implementation features. Calling virtual methods is faster. Calling dynamic ones is slower, but the dynamic methods table (DMT - Dynamic Methods Table) takes up slightly less memory than the virtual methods table (VMT - Virtual Methods Table).

It may seem that calling dynamic methods is not time efficient due to the length of time it takes to look up names. In fact, no name lookup is done during the call, but a much faster mechanism is used using the mentioned table of virtual (dynamic) methods. But we will not dwell on the specifics of the implementation of these tables, since in Java there is no distinction between these types of methods.

Base class Object

The Object class is the base class for all Java classes. Therefore, all its fields and methods are inherited and contained in all classes. The Object class contains the following methods:

public Boolean equals(Object obj)– returns true in the case when the values ​​of the object from which the method is called and the object passed through the obj reference in the list of parameters are equal. If the objects are not equal, false is returned. In the Object class, equality is treated as reference equality and is equivalent to the “==” comparison operator. But in descendants this method can be overridden, and can compare objects by their contents. For example, this happens for objects of shell numeric classes. This can be easily checked with code like this:

Double d1=1.0,d2=1.0;

System.out.println("d1==d2 ="+(d1==d2));

System.out.println("d1.equals(d2) ="+(d1.equals(d2)));

The first line of output will give d1==d2 =false and the second d1.equals(d2) =true

public int hashCode()- issues hash code object. A hash code is a conditionally unique numeric identifier associated with an element. For security reasons, you cannot give the object's address to an application program. Therefore, in Java, a hash code replaces the address of an object in cases where for some purpose it is necessary to store tables of object addresses.

protected Object clone() throws CloneNotSupportedException– the method copies an object and returns a link to the created clone (duplicate) of the object. In the descendants of the Object class, it is necessary to redefine it, and also indicate that the class implements the Clonable interface. Attempting to call a method from an object that does not support cloning causes a CloneNotSupportedException to be thrown. Interfaces and exception situations will be discussed later.

There are two types of cloning: shallow (shallow), when the values ​​of the fields of the original object are copied one-to-one into the clone, and deep (deep), in which new objects are created for fields of a reference type, cloning the objects to which the original fields refer. In shallow cloning, both the original and the clone will reference the same objects. If an object has fields of only primitive types, there is no difference between shallow and deep cloning. The implementation of cloning is carried out by the programmer developing the class; there is no automatic cloning mechanism. And it is at the class development stage that you should decide which cloning option to choose. In the vast majority of cases, deep cloning is required.

public final Class getClass()– returns a reference to a metaobject of type class. With its help, you can obtain information about the class to which an object belongs and call its class methods and class fields.

protected void finalize() throws Throwable– Called before an object is destroyed. Must be overridden in those descendants of Object in which it is necessary to perform some auxiliary actions before destroying the object (close a file, display a message, draw something on the screen, etc.). This method is described in more detail in the corresponding paragraph.

public String toString()– returns a string representation of the object (as adequately as possible). In the Object class, this method produces a string of the object's fully qualified name (with the package name), followed by an '@' character, and then a hexadecimal hash code of the object. Most standard classes override this method. For numeric classes, the string representation of the number is returned, for string classes – the contents of the string, for character classes – the character itself (and not the string representation of its code!). For example, the following code snippet

Object obj=new Object();

System.out.println(" obj.toString() gives "+obj.toString());

Double d=new Double(1.0);

System.out.println(" d.toString() gives "+d.toString());

Character c="A";

System.out.println("c.toString() gives "+c.toString());

will provide a conclusion

obj.toString() gives java.lang.Object@fa9cf

d.toString() gives 1.0

c.toString() gives A

There are also methods notify(), notifyAll(), and several overloaded variants of the method wait, designed to work with threads. These are discussed in the section on streams.


Related information.


Static Linking as an Optimization

In some cases, efficiency is the main requirement, and even the small overhead mentioned above is undesirable. In this case, it can be noted that they are not always justified. Call x.f (a, b, c...) does not need dynamic binding in the following cases:

1 f is not overridden anywhere in the system (has only one declaration);

2 x is not polymorphic, that is, it is not the target of any attachment whose source is of a different type.

In any of these cases, detected by a good compiler, generated for x.f (a, b, c...) the code may be the same as the code generated by the C, Pascal, Ada, or Fortran compilers to call f (x, a, b, c...). No overhead costs will be required.

The ISE compiler, which is part of the environment described in the last lecture, currently performs optimization (1), and it is planned to add (2) (the analysis of (2) is, in fact, a consequence of the type analysis mechanisms described in the lecture on typing).

While (1) is interesting in its own right, its immediate usefulness is limited by the relatively low cost of dynamic binding (see statistics above). The real gain from it is indirect, since (1) allows for a third optimization:

4. Use whenever possible automatic substitution of procedure code.

Such substitution means expanding the program body with the text of the called procedure at the place where it is called. For example, for the procedure

set_a(x:SOME_TYPE) is

Make x the new value of attribute a.

the compiler can generate to call s.set_a(some_value) the same code that the Pascal compiler will generate for assignment s.a:= some_value(not acceptable designation for us because it violates information hiding). In this case there is no overhead at all, since the generated code does not contain a procedure call.

Code substitution is traditionally seen as an optimization that should be specified programmers. Ada includes a pragma (instruction to the translator) inline, C and C++ offer similar mechanisms. But this approach has inherent limitations. Although for a small, static program a competent programmer can determine which procedures can be substituted, for large developing projects this is impossible. In this case, a compiler with a decent algorithm for determining substitutions will far exceed the programmers' guesses.

For each call to which automatic static linking (1) applies, the OO compiler can determine, based on an analysis of the time-memory trade-off, whether automatic procedure code substitution (3) is worthwhile. This is one of the most amazing optimizations - one of the reasons why the efficiency of hand-produced C or Fortran code can be achieved, and sometimes, on large systems, surpassed.

To the efficiency gains that increase with program size and complexity, automatic code substitution adds the benefit of greater reliability and flexibility. As noted, code substitution is semantically correct only for a procedure that can be statically constrained, such as in cases (1) and (2). This is not only acceptable, but also quite consistent with the OO method, in particular with the Open-Closed principle, if a developer, halfway through the development of a large system, adds an override of some component that at that moment had only one implementation. If procedure code is inserted manually, the result may be a program with erroneous semantics (since in this case dynamic linking is required, and inserting code, of course, means static linking). Developers should focus on building correct programs rather than tedious optimizations that, when done manually, lead to errors but can in fact be automated.

One last note about efficiency. Published statistics for object-oriented languages ​​indicate that somewhere between 30% and 60% of calls actually use dynamic binding. This depends on how intensively developers use specific properties of methods. In the ISE system this ratio is close to 60%. With the optimizations just described, you only have to pay to dynamically link only those calls that actually need it. For the remaining dynamic calls, the overhead is not only small (limited to a constant), but also logically necessary - in most cases, to achieve a result equivalent to dynamic binding, you will have to use conditional statements ( if... then... or case...of...), which may be more expensive than the simple array-based mechanism above. So it's not surprising that OO programs compiled with a good compiler can compete with hand-written C code.

From the book Boost your website author Matsievsky Nikolay

Static archiving in action There is a way to get by with just a couple of lines in the configuration file (httpd.conf or .htaccess, the first is preferable), if you spend a couple of minutes and archive all the necessary files yourself. Suppose we have

From the book The C++ Reference Guide author Stroustrap Bjarne

R.3.3 Program and Linking A program consists of one or more files linked together (§R.2). The file consists of a sequence of descriptions. A file-scoped name that is explicitly declared static is local to its translation unit and can

From the book The C# 2005 Programming Language and the .NET 2.0 Platform. by Troelsen Andrew

Dynamic Binding Simply put, dynamic binding, or dynamic binding, is an approach by which you can create instances of a given type and call their members at runtime and under conditions where there is nothing yet known about the type at compile time.

From the book ArchiCAD 11 author Dneprov Alexander G

Linking Views Among ArchiCAD's visualization tools, there is a mechanism whose purpose is to simultaneously display two different views together. What's the point? The need for this arises quite often. For example, to visually link objects

From the book Fundamentals of Object-Oriented Programming by Meyer Bertrand

Dynamic Binding The combination of the last two mechanisms, overriding and polymorphism, directly implies the following mechanism. Let's say there is a call whose target is a polymorphic entity, for example an entity of type BOAT calls the turn component.

From the book System Programming in Windows Environment by Hart Johnson M

Linking to an ADT A class, as has been said many times, is an implementation of an ADT, either specified formally or implicitly. At the beginning of the lecture, it was noted that statements can be considered as a way of introducing into the class semantic properties that lie in

From the book TCP/IP Architecture, Protocols, Implementation (including IP version 6 and IP Security) by Faith Sydney M

Dynamic Binding Dynamic binding will complement overriding, polymorphism and static typing, creating a basic tetralogy

From the book VBA for Dummies by Steve Cummings

A Button by a Different Name: When Static Linking Is Wrong By now, the main takeaway from the principles of inheritance outlined in this lecture should be clear: The principle of dynamic binding When the result of static linking does not match the result

From the book UNIX Operating System author Robachevsky Andrey M.

Typing and Binding Although, as a reader of this book, you will likely be able to tell the difference between static typing and static binding, there are people who cannot do this. This may be partly due to the influence of Smalltalk, which advocates a dynamic approach to both problems

From the C++ book for beginners by Lippman Stanley

Implicit Linking Implicit linking, or load-time linking, is the simpler of the two linking techniques. The procedure for using Microsoft C++ is as follows: 1. After all the functions necessary for the new DLL have been collected,

From the book Linux Kernel Development by Love Robert

Explicit Linking Explicit linking, or run-time linking, requires that the program provide specific instructions about when to load or free the DLL. Next, the program receives the address of the requested

From the author's book

11.9.3 Binding The DHCP server maintains a table of mappings between clients and their configuration parameters. Binding consists of assigning each client an IP address and a set of configuration

From the author's book

Static State The Static keyword in a variable declaration should be used when you want the variable to remain in memory - so that its value can be used - even after the procedure has completed its work. In the following example, the variable

From the author's book

Binding Before a client can call a remote procedure, it must be bound to a remote system that has the required server. Thus, the binding task breaks down into two:? Finding a remote host with the required server? Finding

From the author's book

9.1.7. Safe binding A When using overloading, it seems that a program can have several functions of the same name with different lists of parameters. However, this lexical convenience exists only at the source text level. In the majority

From the author's book

Static memory allocation on the stack In user space, many memory allocation operations, particularly some of the examples discussed earlier, can be performed using the stack because the size of the allocated memory region is known a priori. IN



Share