Let me specify exactly what I'm trying to do, I need to split my foo-bar program into five separate files: main, foo.h, foo.cpp, bar.h, bar.cpp. My header files (foo.h and bar.h) are meant to contain the declarations for their corresponding classes, while the c++ files (foo.cpp and bar.cpp) are meant to define the class.
I'm using Visual Studio, and thus far the only file I have showing red flags is my main file. Here is my code thus far, and I will include the errors that are being thrown in my main file:
main.cpp
#include <iostream>
#include "foo.h"
#include "foo.cpp"
#include "bar.h"
#include "bar.cpp"
using namespace std;
int main() {
Bar b(25); /*I am getting a red flag under the 25, stating there is no constructor that can convert int to Bar*/
b.func1(); /*I'm getting a red flag under func1 and func2 stating neither of them are members of Bar*/
b.func2(34);
return 0;}
foo.h
#ifndef foo_h
#define foo_h
#include "foo.cpp"
class Foo {};
#endif
foo.cpp
#ifndef foo_c
#define foo_c
#include "foo.h"
#include "bar.cpp"
private:
int data;
public:
Foo(int d) : data(d) {}
int get_data() { return data; }
virtual void func1() = 0;
virtual int func2(int d) = 0;
#endif
bar.h
#ifndef bar_h
#define bar_h
#include "bar.cpp"
#include "foo.h"
class Bar : public Foo {};
#endif
bar.cpp
#ifndef bar_c
#define bar_c
#include "bar.h"
#include "foo.h"
#include "foo.cpp"
Bar(int d) : Foo(d) {}
void func1() {
cout << "Inside func1\n";
cout << "\tData is " << get_data() << endl;
}
int func2(int d) {
cout << "Inside func2 with " << d << endl;
cout << "\tData is " << get_data() << endl;
return d;
}
#endif
My program worked until I split it up, but now it keeps throwing this message at me when I try to compile it, and there are a couple of red flags in my main code. This is what the console tells me:
No suitable constructor exists to convert int to Bar
func1 is not a member of class Bar
func2 is not a member of class Bar
My question is: What am I doing wrong, and is there a better way to go about what I'm trying to do?
Thank you in advance.
There is more than one misconception manifested in this code. Perhaps it is easier to correct them altogether than to describe and discuss them individually.
Let us start from the bottom of the dependency tree. There at the bottom is a virtual class Foo. Here is its correct declaration.
#ifndef foo_h
#define foo_h
class Foo {
private:
int data;
public:
Foo(int);
int get_data();
virtual void func1() = 0;
virtual int func2(int) = 0;
};
#endif
Note that we include the declarations of all its methods in the header file. However the implementation of the nonvirtual methods is moved out into the foo.cpp file.
#include "foo.h"
Foo::Foo(int d) : data(d) { }
int Foo::get_data() { return data; }
Note that we do not need any special devices to protect from multiple inclusion of the .cpp file because we are not going to include it ever.
Now Foo is the parent of the class Bar that does some real work for us. Once again, all its methods are declared within the class declaration.
#ifndef bar_h
#define bar_h
#include "foo.h"
class Bar : public Foo {
public:
Bar(int);
void func1();
int func2(int);
};
#endif
And its implementation is in the corresponding compilation unit called bar.cpp. When implementing a class method we indicate which class the method belongs to by prepending the class name to the method name, e.g. Bar::func1.
#include "bar.h"
#include "foo.h"
#include <iostream>
Bar::Bar(int d) : Foo(d) {};
using namespace std;
void Bar::func1() {
cout << "Inside func1\n";
cout << "\tData is " << get_data() << endl;
}
int Bar::func2(int d) {
cout << "Inside func2 with " << d << endl;
cout << "\tData is " << get_data() << endl;
return d;
}
Finally we use it in the main.cpp where only a small change is required.
#include <iostream>
#include "foo.h"
#include "bar.h"
using namespace std;
int main() {
Bar b(25);
b.func1();
b.func2(34);
return 0;}
Let's now proceed with building our project. If you were using GCC that would've been easy to describe as a sequence of CLI commands. Since you are using Visual Studio, you would have to perform the corresponding actions through the GUI.
First compile Foo
g++ -c -Wall foo.cpp
Next compile Bar
g++ -c -Wall bar.cpp
Compile main
g++ -c -Wall main.cpp
Now link it all together
g++ -o main foo.o bar.o main.o
Finally run it and voila
Inside func1
Data is 25
Inside func2 with 34
Data is 25
You should never #include .cpp files. Instead, compile each .cpp file into an object file and link them into an executable.
During the preprocessor stage, the compiler takes all of the #included files and treats them as if they were concatenated into one large program. Sometimes, certain files may be #included multiple times. Declarations, in header files, may be repeated, but multiple definitions, from source files, cause errors. (You probably don't have this problem because you used include guards in your source files.)
When creating object files, header files are used by the compiler to check names and types, but the actual definitions are not needed. The definitions that are found are compiled into the object file. The purpose of separate object files is to separate compilation of these definitions into modular units.
Related
I have a C++ Class A like this:
// in header file A.h
class A
{
public:
void foo1();
void foo2();
private:
double m_a;
};
// in cpp file A1.cpp
void A::foo1(){
m_a = 0; //do something with m_a
}
// in cpp file A2.cpp
void A::foo2(){
foo1(); // call foo1
}
// in cpp file main.cpp
int main(){
A obj;
obj.foo2();
return 0;
}
In my occasion, the function foo1 and foo2 are implemented by two different people. For some reasons, I need to hide the implementation of foo1 so that the people who code A2.cpp cannot fetch the source code of foo1, and at the same time he can use Class A in his own application (like main.cpp above).
I tried to archive A1.cpp as a static library, and of course, an 'unresolved external symbol' error for foo2 occurred.
I built my static library using CMake plugins in Visual Studio 2019, and my CMakeLists.txt is like:
add_library (my_lib STATIC A1.cpp)
target_include_directories(my_lib PUBLIC path/to/A.h)
Is there any solution or workaround for this issue?
You can compile A1.cpp without linking, which will give you a compiled object file, A1.o. You can hand this off, so the developers for A2.cpp won't be able to see the source, but will be able to link your object file to build the final project.
With g++, that could look like:
A.h
class A
{
public:
void foo1();
void foo2();
private:
double m_a;
};
A1.cpp
#include "A.h"
#include <iostream>
void A::foo1() {
std::cout << "foo1 called!" << std::endl;
m_a = 72;
}
A2.cpp
#include "A.h"
#include <iostream>
void A::foo2() {
std::cout << "foo2 called!" << std::endl;
foo1();
}
main.cpp
#include "A.h"
int main() {
A a;
a.foo2();
}
Building the project
If you're using g++, then...
g++ -c A1.cpp
This will compile but not link A1.cpp, giving you the object file A1.o. You can hand that file off so that the other developers can build the whole project with
g++ main.cpp A1.o A2.cpp
My question is very similar to a previously answered question except I'm trying to rewrite the same program using seperate compilation in order to practice proper C++ coding style. I've simplified the program to highlite the problamatic areas.
Currently this program runs fine if I declare the entire class as a friend, but this feels inherently dangerous, giving the entire class friend access.
File Foo.h
#ifndef FOO_H
#define FOO_H
class Foo{
friend class Bar;
public:
// getX defined in header for simplicity.
const int &getX() const{
return x;
}
private:
int x = 0;
};
#endif
File Bar.h
#ifndef BAR_H
#define BAR_H
#include "Foo.h"
class Bar{
public:
void mutateX(Foo &foo);
};
#endif
File Bar.cpp
#include "Bar.h"
void Bar::mutateX(Foo &foo){
foo.x = 1;
}
I can't wrap my head around how to just make the mutateX member as a friend. Any help will be greatly appreciated!
Here's a tester class main.cpp
#include "Foo.h"
#include "Bar.h"
#include <iostream>
int main(){
Foo foo;
Bar bar;
std::cout << foo.getX() << std::endl;
bar.mutateX(foo);
std::cout << foo.getX() << std::endl;
return 0;
}
Why if I have
in foo.h:
class Foo
{
}
void Bar(const Foo& foo);
it works but:
in foo.h:
class Foo
{
}
in bar.cpp
#include "foo.h"
void Bar(const Foo& foo);
doesn't work (unknown type name 'Foo' is its exact words)?
I don't know what about my question isn't specific and forward declarations don't work they just create a error 'duplicate symbol' so im just going to post the code im working with
in creatures.h
#ifndef CREATURES_H_
#define CREATURES_H_
#include <string>
#include "textio.hpp"
class Creature {
private:
protected:
int statBlock[10];
public:
std::string name = "foo";
Creature ();
void ai(int);
};
class Dwarf : public Creature {
private:
public:
std::string name = "Dwarf";
Dwarf (int);
void defaultDwarfGen();
};
main.cpp
#endif
#include "creatures.hpp"
#include "textio.hpp"
#include <iostream>
int main(int argc, char const *argv[]) {
Dwarf creature_1(0);
return 0;
}
textio.hpp:
#ifndef TEXTIO_H
#define TEXTIO_H
#include <iostream>
#include "creatures.hpp"
void challenge(const Creature& param);
#endif
Your problem is that you are including textio.hpp in creatures.hpp so first time that compiler see function void challenge(const Creature& param)Creature class isn't defined.
When you include createures.hppin textio.hpp CREATURES_H_ is already defined and bypass inclusion)
You can fix it deleting this include or declaring a forward definition for Creature class
In order to answer this question properly, you must provide foo.h, foo.cpp, bar.h, and bar.cpp
In short:
To make use of Bar in foo.h, foo.h must have the declaration for Bar.
To make use of Foo in bar.h, bar.h must have the declaration for Foo.
To make use of Bar in foo.cpp, foo.h or foo.cpp must have the declaration for Bar.
To make use of Foo in bar.cpp, bar.h or bar.cpp must have the declaration for Foo.
When I say, "must have declaration for", you can #include the appropriate header.
If you are trying to use Foo in Bar and Bar in Foo, then you've got a circular reference. The way we overcome this is with a forward declaration.
You can read about forward declarations here: https://isocpp.org/wiki/faq/misc-technical-issues#forward-decl
C Example
bb.c:
#include "bb.h"
#include <stdio.h>
void bb() {
printf("aa()...\n");
aa();
}
main.c:
#include "aa.h"
#include "bb.h"
int main(int argc, const char** argv) {
aa();
bb();
return 0;
}
aa.h:
#ifndef aa_h
#define aa_h
#include <stdio.h>
void aa() {
printf("aa()...\n");
}
#endif // aa_h
bb.h:
#ifndef bb_h
#define bb_h
#include "aa.h"
void bb();
#endif // bb_h
C Result
Compiled with clang main.c bb.c:
duplicate symbol _aa in:
/var/folders/f2/2w4c0_n519g8cd2k6xv66hc80000gn/T/main-OsFJVB.o
/var/folders/f2/2w4c0_n519g8cd2k6xv66hc80000gn/T/bb-OkcMzn.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
C++ Example
b.cpp:
#include "b.hpp"
void b::do_something_else() {
std::cout << "b::do_something_else() being called..." << std::endl;
a a;
a.doit();
}
main.cpp:
#include "a.hpp"
#include "b.hpp"
int main() {
a a;
b b;
a.doit();
b.do_something_else();
return 0;
}
a.hpp:
#ifndef a_hpp
#define a_hpp
#include <iostream>
class a{
public:
void doit() {
std::cout << "a::doit() being called..." << std::endl;
}
};
#endif // a_hpp
b.hpp:
#ifndef b_hpp
#define b_hpp
#include "a.hpp"
#include <iostream>
class b{
public:
void do_something_else();
};
#endif // b_hpp
C++ Result
The above compiles fine with clang++ main.cpp b.cpp and the output to the program is:
a::doit() being called...
b::do_something_else() being called...
a::doit() being called...
Questions
Why does the duplicate error not occur with the C++ version?
Does the fact that the function void a::doit() is defined in the header file rather than a source file mean that the compiler will automatically inline the function?
In C++ class methods are not top-level symbols, but are effectively scoped names within their class hierarchy.
This means that you have defined in C++ two doit() methods, a::doit() and b::doit()
In C, you have attempted to define one aa() function twice.
Note that C++ will give an error too if you define the doit() method twice, within the scope of the same class.
#include <iostream>
class a {
public:
void doit() {
std::cout << "hello" << std::endl;
}
void doit() {
std::cout << "goodbye" << std::endl;
}
};
leads to
ed.cpp:11:8: error: ‘void a::doit()’ cannot be overloaded
void doit() {
^
ed.cpp:7:8: error: with ‘void a::doit()’
void doit() {
^
In your C example, aa is defined twice, which violates the "one definition rule". This would be equally true if it were C++.
In your C++ example, a::doit is defined twice, but it is implicitly declared inline. Member functions defined within a class are implicitly inline per [dcl.fct.spec]/3:
A function defined within a class definition is an inline function. ...
inline functions are an exception to the one definition rule (in fact, this is the only meaning of inline required by the standard) per [basic.def.odr]/5.
There can be more than one definition of a ... inline function with external linkage (7.1.2) ... in a program, provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. ...
The requirements essentially boil down to a requirement that the definitions be identical in every translation unit where they appear.
Had you declared aa as inline, similar rules would have applied and your code would have compiled and worked as expected.
Why does the duplicate error not occur with the C++ version?
Because there is no duplication. C++ member functions are scoped by the class they are defined in. b::doit() isn't a duplicate of a::doit().
Does the fact that the function void a::doit() is defined in the header file rather than a source file mean that the compiler will automatically inline the function?
No, but it means it is possible.
I'm learning C++. I'm trying to do an exercise where I define several implementations of a pure virtual class with a single function. I'm having trouble linking the class that uses these implementations.
==> BasicMath.h <==
#ifndef BASIC_MATH_H
#define BASIC_MATH_H
#include<string>
#include<vector>
class BasicMath { };
#endif // BASIC_MATH_H
==> Operation.h <==
#ifndef OPERATION
#define OPERATION
#include<string>
#include<vector>
class Operation {
public:
virtual void perform(std::vector<std::string> vec) = 0;
};
#endif // OPERATION
==> Sum.h <==
#ifndef SUM_H
#define SUM_H
#include "Operation.h"
class Sum: public Operation {
public:
void perform(std::vector<std::string> vec);
};
#endif // SUM_H
==> BasicMath.cpp <==
#ifndef BASIC_MATH_C
#define BASIC_MATH_C
#include <string>
#include <vector>
#include <iostream>
#include "BasicMath.h"
#include "Sum.h"
int main(int argc, char* argv[]) {
Sum op;
}
#endif // BASIC_MATH_C
==> Sum.cpp <==
#ifndef SUM_C
#define SUM_C
#include <vector>
#include <string>
#include <iostream>
#include "Sum.h"
void Sum::perform(std::vector<std::string> vec) {
using namespace std;
int total = 0;
cout << "Total: " << total << "\n";
};
#endif // SUM_C
Compilation:
$ g++ -c Sum.cpp
$ g++ -o BasicMath BasicMath.cpp
/tmp/cc1VXjNl.o:BasicMath.cpp:(.text$_ZN3SumC1Ev[Sum::Sum()]+0x16): undefined reference to `vtable for Sum'
collect2: ld returned 1 exit status
I'm 95% sure I'm doing at least one foolish thing here - but my brain is refusing to tell me what.
I have see this question but have not managed to fix my issue.
I Just encountered the same problem, but my problem was that I had not written the destructor code in my .cpp file.
class.h:
class MyClass {
public:
MyClass();
virtual ~MyClass();
};
class.cpp:
MyClass::MyClass() {}
It just gave me the vtable error message, and implementing the (empty) destructor solved the problem.
[Edit] Thus, the corrected class file looks like this:
MyClass::MyClass() {}
MyClass::~MyClass() {}
You're not including the Sum.o object file on your compile&link line (second g++ use).
That error also happens if you forget the = 0 for pure virtual functions
Error:
class Base {
public:
virtual void f();
};
class Derived : public Base {
public:
virtual void f() {}
};
int main() {
Derived d;
Base *b = &d;
(void)b;
}
No error:
class Base {
public:
virtual void f() = 0;
};
This is because without the = 0, C++ does not know that it is a pure virtual function, treats it as a declaration, expecting a later definition.
Tested on g++ 5.2.1.
Tested as of GCC 11.2.0, the error message changed to:
undefined reference to `typeinfo for Base'
command:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
A couple of people have already pointed out the solution to the problem you've seen.
I'll add something rather different. You only need header guards in your headers. You've included them in your source files as well, where they really don't make sense. For example, I've commented out the lines you really don't need (or even want) in sum.cpp:
//#ifndef SUM_C
//#define SUM_C
//
#include <vector>
#include <string>
#include <iostream>
#include "Sum.h"
void Sum::perform(std::vector<std::string> vec) {
using namespace std;
int total = 0;
cout << "Total: " << total << "\n";
};
//#endif // SUM_C
Just FWIW, instead of perform, I'd use operator():
class Operation {
public:
virtual void operator()(std::vector<std::string> vec) = 0;
};
and (obviously) that's also what you'd overload for Sum. To use it, instead of something like:
Sum op;
op.perform();
You'd use something like:
Sum op;
op();
This is particularly convenient when you combine your class with others (e.g., those in the standard library) that invoke operations like functions, whether they're really functions, or "functors" (classes like this, that overload operator() so syntactically they can be used almost like functions).
I normally encounter this error when I accidentally forget the =0 at the end of one of my functions in a pure virtual class.
You're just compiling BasicMath.cpp without Sum.cpp - your linker has no idea about Sum.cpp. You'll need to compile them both together, i.e. Sum.cpp BasicMath.cpp in one go, or you can compile the .cpp files independently and then create the executable by calling g++ with both .o files.
I have met the problem same as yours , and I solved this problem by adding three lines in the CMakeLists.txt , that is:
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)