pre-defining classes not working when using pointers to classes - c++

I am trying to implement a listener. Because of many cross-references I am trying to avoid including other classes and pre-define them
My listener looks as follows
.h
class Book
{
public:
Book();
private:
std::vector<MyListener *> listeners_;
void Notify();
}
.cpp
Book::Book() {}
void Book::Notify() {
MyListener *p_listener;
for ( int i = 0; i < this->listeners_.size(); i++ ) {
p_listener = listeners_[i];
p_listener->Update(); // ERRORS THROWN HERE WHEN NOT INCLUDING LISTENER.H
}
}
This all works fine when I include the listener.h file
#include "listener.h"
But when I instead pre-declare Listener it doesnt work
class Listener;
It gives me the two errors
C:\CPP\qtTradeSim\qtTradeSim\test\book.cpp:33: error: C2027: use of undefined type 'Listener'
C:\CPP\qtTradeSim\qtTradeSim\test\book.cpp:33: error: C2227: left of '->Update' must point to class/struct/union/generic type
Is there a way to avoid including the Listener header?

In the header file of class Book, you should indeed use a forward declaration of MyListener, as the header only defines an std::vector of pointers to MyListener and does not need to know the full declaration of MyListener.
The implementation file of class Book, however, actually needs the full declaration of MyListener, as it calls its update method, so you would include listener.h in the implementation file of class Book instead of in the header file.

Let's suppose the compiler sees the following code:
class Listener;
std::vector<Listener*> pListeners;
// some code...
for(auto& pListener: pListeners) {
pListener->update();
}
Note, how does the compiler see the Listener has a member function update? The symbol update could not be determined until the compiler see the Listener full declaration. Think if you used update with an argument missing, could the compiler capture this problem without seeing the declaration of update? Thus, it cannot translate the code. If you give a full declaration of the Listener, e.g.
class Listener {
public:
Listener() { // some construction
}
void update() {
// dosth
}
};
The compiler could know the update method, its parameters, the return value, etc., and compile it happily.

Related

C++ member function definition outside the class | duplicate symbols

In one of my classes header file Lfo.h, I have a class definition where I put the member function definition out of the class (It might be better to have a separate .cpp file but it should be ok put here?):
// Lfo.h
class CLfo
{
public:
static int create (CLfo*& pCLfo);
};
int CLfo::create(CLfo *&pCLfo)
{
pCLfo = new CLfo;
return 0;
}
Then I have another class called CVibrato:
// Vibrato.h
class CVibrato
{
public:
static int create (CVibrato*& pCVibrato);
private:
CVibrato();
};
and the .cpp file (in the cpp file, I include Lfo.h because later on the vibrato class will have a lfo member but I haven't implemented right now):
// Vibrato.cpp
#include "Lfo.h"
#include "Vibrato.h"
int CVibrato::create(CVibrato *&pCVibrato)
{
pCVibrato = new CVibrato();
return 0;
}
CVibrato::CVibrato()
{
}
Then I want to create a instance of vibrato class in main()
#include "Vibrato.h"
#include "Lfo.h" // if comment this line out there will be no error, why is that?
int main()
{
CVibrato *vibrato = 0;
CVibrato::create(vibrato);
return 0;
}
However I get a 1 duplicate symbol for architecture x86_64 error. What is duplicated? It seems the reason is in Lfo.h, I put the definition of the member function outside of the class, if I put it inside, the program runs properly. But I cannot understand. In c++, aren't we allowed to do this? By the way, if one of my class (in my case vibrato) is going to have a class member of another class (in this case lfo), should I include the header file of member class in .h (vibrato.h) file or .cpp (vibrato.cpp) file?
Classes are declarations. No code is produced from a declaration. Even if you have a member function in the class, it is treated as if an inline by the compiler. Function bodies can be put in a header but should always be declared as inline. The compiler may not actually inline it, but it will treat it as a single instance for code creation.
Any time you:
void function( ) { }
Code is created for that function. If a header is included more than once the compiler is told to create the code more than once. But all functions must have unique names! So you get the duplicate error. That is why code generating lines belong in the .cpp files.
'inline' tells the compiler not to create immediate code but to create the code at the usage point.
You can't put class method definition directly in a header file, unless you explicitly mark it as inline. Like the following:
// Lfo.h
class CLfo
{
public:
inline static int create (CLfo*& pCLfo);
};
int CLfo::create(CLfo *&pCLfo)
{
pCLfo = new CLfo;
return 0;
}
Or,
// Lfo.h
class CLfo
{
public:
static int create (CLfo*& pCLfo);
};
inline int CLfo::create(CLfo *&pCLfo)
{
pCLfo = new CLfo;
return 0;
}

How to declare a new class in the header and define it in the source file without this errors?

I'm on the situation where I want to create a new class and then use it in another created class (C++), but without using different header or source files: both classes shall be in the same place, one way or another. The main class shall contain only a pointer to the "child" class.
Now I know that in many cases is perfectly possible to define a class in the header file. In fact, if one wants to not just set a pointer to that "child class", but actually use one of its methods already in the header file (e.g., for inline methods), one would actually have to define it in the source file:
class ChildClass
{
public:
bool myFunctions() { return true; }
}
class MainClass
{
private:
ChildClass* poChildClass;
inline bool getResult() { return poChildClass->myFunctions(); }
}
But let's suppose I want just to have that pointer there, without any call to my ChildClass' methods, so I should be able to only declare the ChildClass and later define it in the same .cpp file as MainClass is defined:
//in .hpp
class ChildClass;
class MainClass
{
private:
ChildClass* poChildClass;
}
//in .cpp
class ChildClass
{
public:
bool myFunctions() { return true; }
}
//etc.
In a first moment I don't know what could be there of a problem. But in trying to do so with one of my classes in particular (which is based on Qwt's QwtPlotPicker class), I get some compile errors (in this last version):
error: undefined reference to `vtable for Picker'
The error points out where in the following code (in the .cpp):
class Picker : public QwtPlotPicker
{
Q_OBJECT
public:
Picker( QWidget* canvas ) :
QwtPlotPicker( canvas ) //Here lies the error the compiler says
//...
So what is the problem? What do I get this "undefined reference to 'vtable'" problem?
Thanks for any help,
Momergil
This is a problem I have had forever when using QT. Any class that has the Q_OBJECT macro in it MUST be listed in the HEADERS before running qmake (as far as I can tell). This may even mean putting the .cpp file in the HEADERS section.

How to forward declare third-party struct

In my original code, I refer to the third-party .H in the ClassOne header file and everything works fine. Now, I received a new requirement that doesn't allow me to refer to the third-party .H in the ClassOne header file. So that the consumer of my code (i.e. ClassOne) will not have to indirectly includes the third-party .H file. I have tried the following modification but it doesn't work.
Here is the sample code:
// third_party.h
struct PPP
{
int x;
int y;
}; // without default constructor
// Original code!
//////////////////////////////////////////////
// ClassOne.h // my class
#include <third_party.h> // refer to the .H in header file
namespace X
{
class ClassOne
{
...
private:
boost::scoped_ptr<PPP> m_scpPPP;
};
}
// ClassOne.cpp
#include <third_party.h>
namespace X
{
ClassOne::ClassOne()
{
m_scpPPP.reset( new PPP() ); // fine
}
...
}
// Modified code!
==========================================================
// ClassOne.h
struct PPP; // error C2371: 'PPP' : redefinition; different basic types
namespace X
{
class ClassOne
{
...
private:
boost::scoped_ptr<PPP> m_scpPPP;
};
}
// ClassOne.cpp
#include <third_party.h>
namespace X
{
ClassOne::ClassOne()
{
m_scpPPP.reset( new PPP() ); // now see errors.
// error C2512: 'PPP' : no appropriate default constructor available
}
...
}
Question 1> Where should I forward declare the third-party struct type PPP?
Question 2> Why the compiler now complain about the PPP that has no default constructor?
It is not standard behavior to instantiate templates with incomplete types, therefore it shouldn't work boost::scoped_ptr.
Having said that, unique_ptr has a special rule, allowing to take incomplete types. If you use it (instead of boost::scoped_ptr), then it is done like this :
// forward declaration of PPP, assuming c++ header
struct PPP;
namespace X
{
class ClassOne
{
...
private:
std::unique_ptr<PPP> m_scpPPP;
};
}
Simply put: That won't work. Since you use PPP (and not PPP*) in side your ClassOne, the compiler needs to know the size at that point, so it needs to know the definition of PPP. To hide PPP from the public .h file, you'll have to do more. One possible solution is to hide your implementation class behind another class. Another would be only to refer to PPP* in your class declaration (although that would make the usage of scoped_ptr<> a bit pointless).
The compiler expects a default constructor because he assumes there is one. He needs the definition of the class to call "new" as well. You can work around this problem by moving the implementation of the ctor to the .cpp file, where you may include thirdParty.h.

Forward declaring a C++ class in Objective-C

I am writing an Objective-C class that needs to make function calls on a C++ class instance. I found this suggestion, but if I try it I get an error for having an incomplete definition of type 'struct MyCPlusPlusClass'
struct MyCPlusPlusClass;
typedef struct MyCPlusPlusClass MyCPlusPlusClass;
#interface MyBridgeClass() {
MyCPlusPlusClass *my_CPlusPlus;
}
...
- (id)initWithMrGame:(MyCPlusPlusClass *)cPlusPlus
{
self = [super init];
if (self) {
my_CPlusPlus = cPlusPlusClass;
my_CPlusPlus->p_favorite_integer = 0; // Compiler error
}
return self;
}
The actual definition occurs in a .mm file that's generated by a pre-compiler, just to add another layer of challenge.
How might I get this to work?
EDIT: Interpreting Adam's answer
// in MyCode.h
struct MyCPlusPlusClass; // Forward declaration, no need for #include/#import
#interface MyBridgeClass() {
struct MyCPlusPlusClass *my_CPlusPlus;
}
// in MyCode.m
#include MyCode.h
// in BigGenerateFile.mm
class MyCPlusPlusClass;
class MyCPlusPlusClass { ... }
My goal is to be able to use MyCPlusPlusClass in MyCode.m, but I can't include the .mm file because the compiler gets very unhappy. It may be that the way this thing is architected is going to make me go a different route.
You can't access member variables of incomplete structures/classes. To do so, you need to the full definition. Typically you use forward declarations in header files so that anything that includes that header doesn't pull in lots of unnecessary other header files it won't need, but for source files you usually need the full definitions.
So I'd suggest changing you code to something like this:
// Header file (.h)
struct MyCPlusPlusClass; // Forward declaration, no need for #include/#import
#interface MyBridgeClass() {
struct MyCPlusPlusClass *my_CPlusPlus;
}
// Source file (.mm)
#include "MyCPlusPlusClass.h"
...
// Can now access my_CPlusPlus->p_favorite_integer etc.
You can do a number of things on an incomplete type, but accessing members of an object of that type is not one of them.
A simple possible solution would be a helper function that's defined somewhere where the complete type is available:
void set_p_favorite_integer(MyCPlusPlusClass*, int);
// ...
set_p_favorite_integer(my_CPlusPlus, 0);

Unresolved external symbol [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is an undefined reference/unresolved external symbol error and how do I fix it?
I'm relatively new to C++ (as you can probably tell by the question) and I've hit a problem. I have two files: Drives.h and Drives.cpp
Drives.h
#pragma once
enum MountMode
{
User,
System,
Both,
Auto
};
class Drive
{
public:
Drive(void);
~Drive(void);
BOOL Mount(MountMode mode);
VOID Unmount(void);
BOOL IsConnected(void);
static char* DeviceName;
static char* DrivePath;
};
class Drives
{
public:
Drives(void);
~Drives(void);
};
and my Drives.cpp:
#include "stdafx.h"
#include "Drives.h"
Drives::Drives(void)
{
Drive USB0; //Error happening here
}
Drives::~Drives(void)
{
}
The error is saying that the Drives class constructor, destructor and IsConnected() are all unresolved externals. I'm not sure what I'm missing since I set this class up like the one on cplusplus.com
Thanks in advance
As the error message says, you have not implemented the constructor and destructor of Drive:
Drive::Drive(void) {
...
}
Drive::~Drive(void) {
...
}
Creating a local variable of class type (as you do in Drive USB0;) will invoke that class' constructor, and the destructor will be invoked at the end of the variable's scope; hence the error.
You should implement the other functions of Drive too - declaring a function in a class declaration is essentially a promise that the function will be implemented somewhere.
Yes, those methods have been declared in the Drive class in your header file, but you haven't actually created a body for these methods.
You must either create a body inline in your header file, create a body in a CPP file, or make sure you are linking with an existing file that defines these methods. Otherwise, the error is right, these methods have not been defined.
An Unresolved External Symbol error usually means you have provided a declaration of a function but not its definition.
In your case, since you declared Drive(void) and ~Drive(void) the compiler removes its defaults and expects your definitions to exist, which they don't, so it throws an error.
As a side note: using void in place of empty parenthesis to mean "This function takes no arguments" is a C-Style definition and should not be used.
Also,do not use #pragma once as a substitute for include guards. It is a Microsoft-Specific construct and is not compatible with other compilers. Use actual include guards instead:
#ifndef CLASS_NAME_H
#define CLASS_NAME_H
//CODE HERE
#endif
In the following code you declare two classes(Drive and Drives), but you provide the implementation only for one (Drives)
#pragma once
enum MountMode
{
User,
System,
Both,
Auto
};
class Drive
{
public:
Drive(void);
~Drive(void);
BOOL Mount(MountMode mode);
VOID Unmount(void);
BOOL IsConnected(void);
static char* DeviceName;
static char* DrivePath;
};
class Drives
{
public:
Drives(void);
~Drives(void);
};
To get rid of the error message, you must include an implementation for Drive's class methods. On way to extend your Drives.cpp so that your code may work looks like this:
#include "stdafx.h"
#include "Drives.h"
//Drive class constructor
Drive::Drive(void)
{
//Add initialization code here. For example:
DeviceName = "Name";
DrivePath = "";
}
//Drive class destructor
Drive::~Drive(void)
{
}
//Also add the implementation for Mount
BOOL Drive::Mount(MountMode mode)
{
//implementation for Mount. For example:
return FALSE;
}
//Also add the implementation for Mount
VOID Drive::Unmount()
{
//implementation for Unmount
}
//Also add the implementation for Mount
BOOL Drive::IsConnected()
{
//implementation for IsConnected.For example:
return FALSE;
}
//Drives class constructor
Drives::Drives(void)
{
Drive USB0; //Error happening here
}
//Drives class destructor
Drives::~Drives(void)
{
}
It is also possible if you copy paste-d the code, that you also have the implementation for the Drive class but you save it in another .cpp file, like Drive.cpp. In that case you should either copy all the implementation methods from the other Drive.cpp file to Drives.cpp. Or you should move the declaration of Drive class from Drives.h to Drive.h. In that case you will have clear separation for classes in different files, which is good, but you will have to include Drive.h in the Drives.h file.