Reputation: 3260
I am reading a snippet of code and surprised that the following code (as a minimal example) compiles, and compelled to ask is it legit to override a function without providing a function body? If yes, then is this an intentional design for some code logic by the code writer?
class Parent {
public:
virtual void func1(){std::cout<< "Parent" << std::endl;};
};
class Child: public Parent {
public:
void func1() override; // override, without function body
};
int main(){
Child c;
// c.func1(); // the code compiles is this line is commented.
}
Reply to the comments
First thanks for a detailed explanation by Jerry Coffin.
Although I feel a need to write something about the comments under the main post, I understand that it is generally that the definition of function is written in cpp file, and I indeed looked for the definition in the cpp file before posting here, my main concern about this code is that this code might be intentionally written in this way, yes, although there is linking error, but this linking error might be intended by the coder to hint that this function should not be called. I, as a junior developer in C++ which is known for its complexity, am genuinely asking if this thought is a well-recognized design.
Upvotes: 0
Views: 377
Reputation: 119877
Edit: The code as posted is somewhat buildable (with gcc, if an #include
is added and optimisations are enabled), but the explanation below is of little relevance since there is a linker error in real code. I'm leaving it here for sake of completeness. (End edit)
The program looks invalid, why is it ever compiled?
There are two answers to this question, a theoretical one (based on the language standard) and a practical one (based on how compilers and linkers commonly work).
The theoretical answer is as follows. The standard doesn't prescribe any behaviour for this program. Any declared virtual function must be defined, but if a definition is missing, no diagnostic is required (3.2 basic.def.odr "One definition rule"). So an implementation is free to do anything at all with this program, for example, accept it as if there's nothing wrong with it.
The practical answer says that all references to the function are optimised away, so no definition is needed. It is the first virtual function in the class, and so it controls when and where the virtual table for that class is emitted. Since it is not defined, no virtual table is emitted, but since no virtual calls are made, the vtable is never used. The linker never gets to know it was ever needed. Adding and using a different virtual fumction or changing an optimisation level may (or may not) lead to the compiler emitting a reference to the function and so revealing the error.
Upvotes: 1
Reputation: 490178
If the call to c::func()
is active (not commented out), and the rest left as-is, the code won't build (at least with any tool chain I've seen). Specifically, it'll compile fine, but when you get to the linking stage, the missing c::func1
will result in an unresolved external error.
The obvious way this would happen would be an accident in editing, probably in the process of splitting the code into a header and an implementation file, or possibly consolidating code from a header and implementation file (plus some "client" code to instantiate and use the class) into a single file for posting (or similar).
In real code, you'd typically expect to see the class definitions in a header, and the implementation(s) in a matching source file, something like this:
parent.h:
#ifndef PARENT_H_INC_
#define PARENT_H_INC_
class Parent {
public:
virtual void func1();
};
#endif
child.h:
#ifndef CHILD_H_
#define CHILD_H_
#include "parent.h"
class Child: public Parent {
public:
void func1() override; // override, without function body
};
#endif
parent.cpp:
#include "parent.h"
virtual void parent::func1() {
std::cout<< "Parent" << std::endl;
}
child.cpp:
#include "child.h"
void child::func1() override {
// Programmer has learned to prefer "\n" over std::endl;
std::cout << "child\n";
}
main.cpp:
#include "child.h"
int main() {
Child c;
c.func1();
}
One of the difficulties of C++ is the need for manually splitting up the code like this, and keeping multiple files properly coordinated (though, in fairness, it does have some advantages as well).
Upvotes: 1