Advertisement

Simple inheritance

Started by August 02, 2015 10:59 AM
4 comments, last by SeanMiddleditch 9 years, 5 months ago

Hello. I have a class A that has some virtual functions that are defined there but in a cpp file. So the declaration of the virtual function is in .h file in the A class and the definition is in .cpp file A::functio() { do something }.

When I create another class for example B: public A and I dont want to overwrite the virtual function from A, the B class doesnt see the implementation of the function in class A(Linker errors). It only sees the implementation if I include the .cpp file of the class A. So If I have virtual function defined in the base class and sometimes I dont want to overwrite it just use it as it was implemented in the base class how the second class sees the implementation ?

This should not be happening. Can you post the full source code (h/cpp-files for both classes and how you use them in eg. a simple main function), so we can see what exactly causes this issue?

Advertisement

what shouldn't be happening?

the OP said, " It only sees the implementation if I include the .cpp file of the class A" that had the definition.

I'm assuming that when you say "include the .cpp" you mean "add the .cpp" to the project.

and now for the fuzzy....


So If I have virtual function defined in the base class and sometimes I dont want to overwrite it just use it as it was implemented in the base class how the second class sees the implementation

probably looking for something like this.


B myB;
myB.A::foo();
and btw, when you implement a derived virtual, it doesn't 'overwrite' <--(OPs own term) the base implementation. It will still be there.
I only use the inheritance concept in it's most basic form and rarely for more than one child depth. But if you're a sibling...the back of the line is way over there. smile.png
[/joke]
resources:
V V V thank you so much for the special attention. (-1) for not adding anything new towards the OP's original question.

Dev careful. Pixel on board.

what shouldn't be happening?
he said, " It only sees the implementation if I include the .cpp file of the class A" that had the definition. No surprise there.

The reported linker errors shouldn't happen, and including an implementation file from an implementation file should not be done in general. It hints at a wrong project set-up. Either the sense of partial project compilation is lost, or else (legitimate) linker errors occur as soon as the base class is inherited from more than 1 derived class.

probably looking for something like this.
B myB;
myB.A::foo();

Although this is a valid invocation, doing like so is (a) not necessary for the given problem situation, and (b) hides a potential problem. Such invocation states explicitly that A::foo should be invoked even in the case that B::foo exists! That implies that the client has a deep understanding of the inner working of the class hierarchy; woe betide anyone who does this invocation without good reason. The situation that B does not override A::foo is definitely not a good reason alone. So mentioning this kind of "solution" should be done with the respective warning what actually happens.


and btw, when you implement a derived virtual, it doesn't 'overwrite' the base implementation. It will still be there.

With respect to the C++11 keyword, an allowed term would be "overriding".

My mistake. I forgot to export the symbol fromn the library. I was building a library and that method was not exported. Thanks for the tips.

I also have another question about inheritance. I want to make a system so that the user can make a vector with "windows". the window element have the description of the window but also I need to store the information about behavior on different action and I dont know how to proceed with that. For example when the user of the class adds a window with a function (CreateNewWindow(width.. etc) actually adds an struct object (with window description) to the vector that holds the "windows". But the users must also add behavior function like OnExit(), OnPaint(), OnResize() etc etc so I must update the Window Procedure function with the ewquested functions:

LRESULT wProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 
switch (msg)
{
case WM_CREATE:
{
 
void ClientRequestedOnCreateFunction()
 
return(0);
} break;
 
 
case WM_DESTROY:
{
 
void ClientRequestedOnDestroyFunction();
PostQuitMessage(0);
return(0);
} break;
 
}
return(DefWindowProc(hwnd, msg, wparam, lparam));
}
 

Is working with function pointers a good approach ? I was thinking that inheritance doesn't work so good as I have n-windows and I don't want the user to manually create so many classes inherited and pass them to the vector.

I also have another question about inheritance.


This isn't a rule of the site, but I'd generally recommend making new topics for new questions. It makes it easier for other new users who are Googling the same problem, and it increases the number of eyeballs you'll get from people who could help with your new question (who may not bother looking at the topic based on the title or old question).

I want to make a system so that the user can make a vector with "windows". the window element have the description of the window but also I need to store the information about behavior on different action and I dont know how to proceed with that.


You're roughly asking for a signals or delegate system.

The rough C and old C++ way to do this is to use raw function pointers. More modern C++ should use std::function. The _bad_ object-oriented way is to use virtual methods.

A more flexible way is to actually use a signals+slots library. You can get one from Boost if you don't mind depending on another giant header-only (read: slower to compile) library.

There's also a bunch of other signals+slots libraries for C++, though I can't attest to the quality of most of them.

Once you're more comfortable with C++, writing your own is also rather easy. The Signal.h in my toy code is only around 50 lines of code.


If you're brave, here's some adapted (and untested) code modified to use the standard C++ types (and also with some error-checking removed, as it depended on some custom assertion macros):

#include <functional>
#include <vector>
#include <memory>
#include <algorithm>

template <typename...> class Signal;

/// Derive from this to be able to attach methods of a class to Signal.
class Slotted
{
	std::shared_ptr<int> m_Lifetime;

public:
	std::weak_ptr<int> GetSlottedLifetime()
	{
		if (!m_Lifetime)
			m_Lifetime = std::make_shared<int>(/*unused*/0);
		return m_Lifetime;
	}
}

/// Connect callbacks to a signal to get notified of a specific event.
template <typename... ParamsT>
class Signal<void(ParamsT...)>
{
	using Signature = void(ParamsT...);
	std::vector<std::pair<std::weak_ptr<int>, std::function<Signature>>> m_Callbacks;

public:
	template <typename... InputT>
	void operator()(InputT&&... argv)
	{
		for (auto& callback : m_Callbacks)
			if (auto _= callback.first.lock()) // ensure the object hasn't/won't-be expired
				callback.second(std::forward<InputT>(argv)...);

		m_Callbacks.erase(std::remove_if(m_Callbacks.begin(), m_Callbacks.end(), [](auto& callback){ return callback.first.expired(); }), m_Callbacks.end());
	}

	template <typename ObjectT, typename... InputT>
	void connect(ObjectT& receiver, void (ObjectT::*method)(InputT...))
	{
		return connect(receiver, [=](auto&&... argv){ return (receiver.*method)(std::forward<decltype(argv)>(argv)...); });
	}

	template <typename FunctorT>
	void connect(Slotted& slotted, FunctorT&& functor)
	{
		m_Callbacks.push_back({slotted.GetSlottedLifetime(), std::forward<FunctorT>(functor)});
	}
};
That doesn't use slots in the usual sense, but rather just allows you to attach signals to any object that derives from Slotted. You can also, via the second connect() overload, use some _other_ object to track lifetime for a given functor, which has all kinds of uses (for instance, in a component-based engine, you can use the base GameObject for lifetime tracking of signals attached to component methods, rather than needing each component to be a shared_ptr-owned object).

Note that yes, the shared_ptr/weak_ptr usage is a little weird, but there's no good workaround for that which doesn't involve new extra code for lifetime tracking (which is what I do in my code) or forcing all your objects to themselves use shared ownership (which is terrible, don't do that).

The code above doesn't let you remove individual connected signals. I had that code, but it ended up being unused, so I stripped it out as part of an optimization pass. There's some further optimization that can be done (removing the second pass for the std::remove_if for instance).

Note also that the code is not thread-safe and also not re-entrant, so be wary of those. You can make it re-entrant easily enough, and thread safety could be achieved with some mutexes.

Sean Middleditch – Game Systems Engineer – Join my team!

This topic is closed to new replies.

Advertisement