简体   繁体   中英

How does main.cpp see this?

Just started out in C++, so bit of a noob, and not sure why this works. How can main.cpp see to use the print() function contained in the separate print.cpp file? I thought you had to use #include/header files or something like that? I'm using Visual Studio if that helps.

main.cpp

#include "stdafx.h"
#include <iostream>
#include <string>

void print(std::string message);

int main()
{
    std::cout << "Enter message: ";
    std::string message = "";
    std::getline(std::cin, message);
    print(message);
    return 0;
}

print.cpp

#include "stdafx.h"
#include <iostream>
#include <string>

void print(std::string message)
{
    std::cout << "Your message is - " << message << std::endl;
}

Actually the code in main.cpp does not "see" the print function in print.cpp at all!

The call to print is only checked by the compiler against the incomplete function specification you wrote earlier in the file, not against anything from any other file. C++ allows this incomplete specification as a way to say, "well I'm not telling you how this function is implemented now, but it should be available to you after this file and all other files are compiled and ready to link together, perhaps with some existing libraries."

You mentioned include files. All an include directive does is (among other things) place a bunch of partial function specifications directly inside your program. After including (which runs as a pre-processing phase before the compiler runs), you will have some code that looks just like your main.cpp above. In fact, to the C++ compiler, your code looks no different than one in which your incomplete function specification of print was replaced with an #include directive of a file containing that specification.

An interesting thing about writing incomplete function specifications is that functions implementing those specifications can often be written in different languages, as long as their data types map directly to C++ types. In your case, std::string binds you directly to C++, but had you used int or even char* an external program in assembly language or C could have been used!

The reason that you can compile code in separate translation units is linkage : Linkage is the property of a name, and names come in three kinds of linkage, which determine what the name means when it is seen in different scopes:

  • None: the meaning of a name with no linkage is unique to the scope in which the name appears. For example, "normal" variables declared inside a function have no linkage, so the name i in foo() has a distinct meaning from the name i in bar() .

  • Internal: the meaning of a name with internal linkage is the same inside each translation unit, but distinct across translation units. A typical example are the names of variables declared at namespace scope that are constants, or that appear in an unnamed namespace, or that use the static specifier. For a concrete example, static int n = 10; declared in one .cpp file refers to the same entity in every use of that name inside that file, but a different static int n in a different file refers to a distinct entity.

  • External: the meaning of a name with external linkage is the same across the entire program. That is, wherever you declare a specific name with external linkage, that name refers to the same thing. This is the default linkage for functions and non-constants at namespace scope, but you can also explicitly request external linkage with the extern specifier. For example, extern int a; would refer to the same int object anywhere in the program.

Now we see how your program fits together (or: "links"): The name print has external linkage (because it's the name of a function), and so every declaration in the program refers to the same function. There's a declaration in main.cpp that you use to call the function, and there's another declaration in print.cpp that defines the function, and the two mean the same thing, which means that the thing you call in main is the exact thing you define in print.cpp.

The use of header files doesn't do any magic: header files are just textually substituted, and now we see precisely what header files are useful for: They are useful to hold declarations of names with external linkage, so that anyone wanting to refer to the entities thus names has an easy and maintainable way of including those declarations into their code.

You could do entirely without headers, but that would require you to know precisely how to declare the names you need, and that is generally not desirable, because the specifics of the declarations are owned by the library owner, not the user, and it is the library owner's responsibility to maintain and ship the declarations.

Now you also see what the purpose of the "linker" part of the translation toolchain is: The linker matches up references to names with external linkage. The linker fills in the reference to the print name in your first translation unit with the ultimate address of the defined entity with that name (coming from the second translation unit) in the final link.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM