Actually I'm new to C++. I tried something out (actually the map container) but it doesn't work the way I assumed it will... Before posting my code, I will explain it shortly.
I created 3 classes:
ClassA
ClassDerivedA
ClassAnotherDerivedA
The two last ones are derived from "ClassA".
Further I created a map:
map<string,ClassA> test_map;
I put some objects (from Type ClassDerivedA and ClassAnotherDerivedA) into the map. Keep in mind: the mapped value is from type "ClassA". This will only work because of Polymorphism. Finally I created an iterator which runs over my map and compares the user input with my keys in the map. If they match, it will call a specific method called "printOutput".
And there is the Problem:
Although i declared "printOutput" as "virtual" the only method called is the one from my base class, but why?
and here is the code:
#include <iostream>
#include <map>
using namespace std;
class ClassA
{
public:
virtual void printOutput() { cout << "ClassA" << endl; }
};
class ClassDerivedA : public ClassA
{
public:
void printOutput() { cout << "ClassDerivedA" << endl; }
};
class ClassAnotherDerivedA: public ClassA
{
public:
void printOutput() { cout << "ClassAnotherDerivedA" << endl; }
};
int main()
{
ClassDerivedA class_derived_a;
ClassAnotherDerivedA class_another_a;
map<string,ClassA> test_map;
test_map.insert(pair<string,ClassA>("deriveda", class_derived_a));
test_map.insert(pair<string,ClassA>("anothera", class_another_a));
string s;
while( cin >> s )
{
if( s != "quit" )
{
map<string,ClassA>::iterator it = test_map.find(s);
if(it != test_map.end())
it->second.printOutput();
}
else
break;
}
}
The problem is slicing. You are storing ClassA values in your map. When you store derived class instances into the map, the get sliced into ClassA objects. You'll need to store pointers in your map instead of values.
See this for more info on slicing: What is object slicing?
C++ is not Java. You cannot store a derived type in a variable of a base type. For example:
Base b = Derived();
will only store the Base part of Derived in the variable b. In order to get polymorphic behaviour, you would need to use pointers, and create the derived class dynamically:
Base * b = new Derived();
The same goes for C++ containers - you need:
map <string, Base *> m;
All of this should be covered in every introductory C++ text book - which one are you using?
You are experiencing "slicing". To get the virtual functions to work properly, you need to call them using a pointer or a reference. In other words, your map should contain pointers to ClassA:
map<string, ClassA *> test_map
Please remember to delete them when you are done, or use smart pointers.
Here's more on slicing: here, here, and here
Related
I'd like to have a derived class update when an object of type base is updated because they share the same reference to the base. This would be done after the base class is already created.
class Tumor : sc2::Unit {
public:
Tumor(const sc2::Unit *unit) : pointer(unit){}
~Tumor()
float spread = 10.0f;
float vision = 11.0f;
// Other things...
bool operator==(const Tumor& rhs) { return pointer->tag == rhs.tag; }
const sc2::Unit *pointer = nullptr;
};
Rather than accessing
tumor.pointer->tag
I'd like it to be:
tumor.tag
sc2::Unit has a tag variable and when I put
std::cout << "tumor_tag Address: " << &(this->pointer->tag) << "\tunit_tag Address: "
<< &(unit->tag) << std::endl;
in the constructor I would have the same memory location being output.
I'm aware I could store a pointer to sc2::Unit in Tumor (as is shown) but I was trying to find a more elegant solution and my Google-foo is weak or it's not possible.
You can't use the inheritance to share the same instance of the base between multiple instances of derived classes because by the definition derived class creates its own instance of the base.
If you don't want use the tumor.pointer->tag construction, you can write a getter function:
tag_type& Tumor::GetTag() {
return pointer->tag;
}
So you'd use it tumor.GetTag().
So I started a small little project to work on while I am learning. Basically, what I'm trying to do is a small "game" which I plan to build on as I learn new things.
Here is a brief description and my problem.
Basically, I want to assign various Hero types to a player based on their choice.
I made a base class "Hero" with only a HP parameter so far. After that, I made 2 derived classes from Hero, HeroType1, HeroType2 which will have specific abilities and so on.
I decided on storing various hero types in std::vector<Hero*> Heroes. Basically, I start my "game" by calling initializeHeroes function which, depending on the player choice creates a new object of type NewHero1 or NewHero2 and stores it an the vector mentioned before. The thing is, no matter what I tried so far, I can't access derived member functions when I want to use them later, only those of the Hero class.
What feels like a good solution: declare global variables player1, player2 and assign to them after players choose the HeroType. However, I can't do that because the data type has to be known before compiling. Sorry if this is a stupid and basic question, my knowledge is still very limited and that is why I am asking for some hints here.
I'd kindly like to ask on how would you approach this, I know it is a very simple issue, but I'm still a beginner and I'm trying to figure out the best way to solve this. Thanks in advance.
If you would like to call a member function from a element from std::vector<Hero*> Heroes and you know somehow that this element points to a Hero2-type, then you could create a new temporary variable Hero2 * tmpPtr and set this variable to the element whose memberfunction you want to call (tmpPtr = Heroes[i]). Then you should be able to call a memberfunction like this: tmpPtr->hero2Memberfuncion().
Full code:
#include <iostream>
#include <vector>
class SomeClass
{
public:
void a() {
std::cout << "a" << std::endl;
}
};
class AnotherClass : public SomeClass
{
public:
void b() {
std::cout << "b" << std::endl;
}
};
void main() {
std::vector<SomeClass *> vec;
AnotherClass v;
vec.push_back(&v);
AnotherClass * tmpPtr = (AnotherClass *)vec[0];
tmpPtr->b(); //Output: "b"
}
However if you want for example loop through the whole vector and for every element run a memberfunction that has the same name but the body of that function differs depending on to what Hero-type the element points, then you may want to use virtual functions. Example:
#include <iostream>
#include <vector>
class SomeClass
{
public:
virtual void a() {
std::cout << "from SomeClass" << std::endl;
}
};
class AnotherClass : public SomeClass
{
public:
void a() {
std::cout << "from AnotherClass" << std::endl;
}
};
void main() {
std::vector<SomeClass *> vec;
AnotherClass v1;
vec.push_back(&v1);
vec[0]->a(); //Output: "from AnotherClass"
SomeClass v2;
vec.push_back(&v2);
vec[1]->a(); //Output: "from SomeClass"
}
I'm trying to achieve something illustrated with this piece of code, but I can't get it to work. I can't return an Animal, and I really don't think returning an Animal* would be a good solution, because I'd have to new and delete manually.
EDIT: I updated the question with a more specific code snippet, demonstrating why returning Animal* isn't a really good idea.
As you can see from the snippet, I basically want to use an Animal as
an interface for a bunch of data, and that data may be part of an array
or it may be a more conventional object.
EDIT 2: Working example: http://ideone.com/4qp3qT
Remaining problems:
duplicate code (FeedAnimals())
performance?
having to delete manually (for the cats)
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <stdlib.h>
using namespace std;
class Animal {
virtual void eat() = 0;
};
// Dog is more high-level
class Dog: public Animal {
char attr1;
char attr2;
Dog(_attr1, _attr2):
attr1(_attr1),
attr2(_attr2)
{}
Dog():
attr1('d'),
attr2('g')
{}
void eat() {
cout << "Dog ate food (" << attr1 << attr2 << ").\n";
}
void barf() {
cout << "Whaf";
}
};
// Cat is more low-level
class Cat: public Animal {
// A cat can basically only exist in a cage
// and it's attributes are defined by it's location
// in the Cage's underlying array. A Cat should also
// have to 2 attributes (both char), so this would
// be kind of true:
// sizeof(Cat) == 2
char* ptr;
// I will only use a Cat in a context of arrays, so
// no need to care about memory management here.
Cat(char* _ptr):
ptr(_ptr)
{}
void eat() {
cout << "Cat ate food (" << *ptr << *(ptr+1) << ").\n";
}
void meow() {
cout << "Meow.";
}
};
class Cage {
virtual Animal GetRandomAnimal() = 0;
};
// DogCage uses a nice (more high level) vector
class DogCage {
vector<Dog> dogs;
DogCage():
dogs(5)
{}
Animal GetRandomAnimal() {
// ?
}
}
// CatCage uses a more low level pointer together with
// malloc etc.
class CatCage {
char* cats;
CatCage():
cats((char*) malloc(4*2)) // room for 4 cats
{}
~CatCage() {
free(cats);
}
Animal GetRandomAnimal() {
// ...
// This is why it's difficult to return a pointer
// to an Animal. I basically want to use a Cat as
// a simple interface to a part of an array.
// If I use pointers etc, that seems like it would
// hit performance.
return Cat(cats+(random*2))
}
}
void FeedAnimals(Animal& a) {
a.eat();
}
int main() {
Cage cage; // ?
string s;
cout << "Cats or dogs?";
cin >> s;
if (s=="Cats") {
cage = CatCage(); // ?
} else {
cage = DogCage(); // ?
}
// fill cage with animals (either cats or dogs)
FeedAnimals(cage.GetRandomAnimal());
}
Your real problem is that you're arbitrarily trying to avoid using the tools (pointer, reference, or smart pointer) available for the job.
I'm guessing, this is because you are familiar with some other language that seems to permit this. The thing is, languages which do such things do it by blending concepts of object, reference, and pointer in ways that C++ does not.
It is not possible in C++ to return an abstract class by value. Period. Returning a class by value means it is necessary to instantiate it and an abstract class is one that cannot be instantiated.
If your function returns a raw reference or pointer, it is necessary that the objects exist when the caller uses them, and that the objects are released (cease to exist) when no longer needed. Either that means your function accepts responsibility for managing object lifetime (e.g. the object is an element of an array) or that the caller is (e.g. function dynamically creates object, caller releases it when done).
Returning a smart pointer means returning an object that manages lifetime of the contained object. The function creates the object, hands it to a smart pointer, and the smart pointer is returned to the caller. When the smart pointer no longer exists (e.g. caller returns so it passes out of scope), the object is released.
Pointers don't have to point to dynamic objects, and only need delete if they do (in which case consider a smart pointer).
In the case of the animals, you can just return a reference or pointer to one of the animals in the cage.
Animal & GetRandomAnimal() {
return cats[GetRandomIndex()];
}
For the cage itself, dynamic allocation is the easiest way to make it polymorphic; but you should use a smart pointer to avoid faffing around with delete and debugging memory leaks.
std::unique_ptr<Cage> cage;
if (s=="Cats") {
cage.reset(new CatCage); // C++14: cage = std::make_unique<CatCage>();
} else {
cage.reset(new DogCage); // C++14: cage = std::make_unique<DogCage>();
}
If you're stuck in the past, replace unique_ptr with auto_ptr, boost::scoped_ptr, or a hand-rolled equivalent.
You can't return an Animal because you can't create an Animal (it's an abstract type). You need something that any Animal (Cat, Dog, etc.) can hide behind: a pointer or a reference. If you create all your animals beforehand, you could return a (preferably const) reference:
Cat cat;
Dog dog;
Animal const& GetRandomAnimal()
{
if (rand() == 42) {
return cat;
}
else {
return dog;
}
}
However, if you want to return an animal object (instance) which didn't exist before, your only option is to create on the heap and return a pointer. Returning a pointer has the problem that it's unclear who is responsible for deleting the object. With C++11, your best bet is to return a smart pointer:
std::unique_ptr<Animal> GetRandomAnimal()
{
if (rand() == 42) {
return std::make_unique<Cat>(/*args to the cat constructor*/);
}
else {
return std::make_unique<Dog>(/*args to the dog constructor*/);
}
}
or use std::shared_ptr and std::make_shared.
(alas, std::make_unique is C++14)
I have a class that holds a vector, which also inherits another class:
class txtExt : public extention
{
private:
string openedFile_;
public:
vector<string> txtVector; //the vector i want to call
};
I fill the vector in a method within a class:
class Manager : public extention
{
// there is some other code here that I know does work
// and it calls this function:
void organizeExtention(string filename, string ext)
{
if(ext == "txt")
{
txtExt txtExt;
txtExt.txtVector.pushback(filename);
}
}
}
and this is my main class where i attempt to call the vector:
int main()
{
// some code here that does previous operations like getting the path
// and filling the vector
// I've tried many ways of trying to call the vector
// here is an example of one:
vector<txtExt*> testVector;
for(int i = 0; i < testVector.size(); ++i)
{
cout << testVector[i] << endl;
}
return 0;
}
I have a few questions:
Am I calling the vector wrong?
Is my vector empty?
Do I have to make my vector global, so other classes can see it?
Note: I've been able to print out the vector where I load the vector using a very simple for loop
Well, as has been said you have a few errors in the code posted, and you maybe have some misunderstandings as well. But to answer the question asked, this
testVector[i]->txtVector
is the way to access the txtVector object that is inside each of your txtExt objects.
If that doesn't work for you then it's because one of the other errors/misunderstandings you have in your code.
To summarize:
reread the first chapters of a good C++ book ( The Definitive C++ Book Guide and List ), then try try to fix your program and deal with each error one at the time.
There are several errors in your code.
First of all, there's no operator << for printing entities of the type txtExt*.
Even object of type txtExt is not printable just like that.
In addition, the testVector you made is empty, so no .size() will be zero, and there's going to be no looping.
Are you really sure that you like to inherit both your classes from 'extension' ?
You can't call a vector, you can access it.
Having a data member (like the vector) public is not a good idea.
Calling a variable by the same name as a class is a very bad idea.
I have trouble guessing what your code should do. Here's a simple example of things you need to understand:
#include <iostream>
#include <vector>
#include <string>
class TxtExt
{
public:
std::vector<std::string> txtVector;
};
int main(){
TxtExt oneTxtExt;
oneTxtExt.txtVector.push_back("hello");
oneTxtExt.txtVector.push_back("world");
for( auto &i : oneTxtExt.txtVector ){
std::cout << i <<std::endl;
}
}
The following code is correct, but has absolutely no effect. You could as well just write {}:
{
TxtExt TxtExt;
TxtExt.txtVector.pushback(filename);
}
You here create a new object, push back to it (btw it is called push_back), but then the object is destroyed at the end of the scope. Also, don't name you objects the same as the class, it becomes really confusing.
I'll phrase this in the form of an example to make it more clear.
Say I have a vector of animals and I want to go through the array and see if the elements are either dogs or cats?
class Dog: public Animal{/*...*/};
class Cat: public Animal{/*...*/};
int main()
{
vector<Animal*> stuff;
//cramming the dogs and cats in...
for(/*all elements in stuff*/)
//Something to the effect of: if(stuff[i].getClass()==Dog) {/*do something*/}
}
I hope that's sort of clear. I know about typeid, but I don't really have any Dog object to compare it to and I would like to avoid creating a Dog object if I can.
Is there a way to do this? Thanks in advance.
As others has noted, you should neither use the typeid, nor the dynamic_cast operator to get the dynamic type of what your pointer points to. virtual functions were created to avoid this kind of nastiness.
Anyway here is what you do if you really want to do it (note that dereferencing an iterator will give you Animal* . So if you do **it you will get an Animal&):
for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
if(typeid(**it) == typeid(Dog)) {
// it's a dog
} else if(typeid(**it) == typeid(Cat)) {
// it's a cat
}
}
Note you can apply the typeid operator to types itself too, as shown above. You don't need to create an object for this. Also note the typeid way doesn't work if you pass it a pointer like typeid(*it) . Using it like that will give you just typeid(Animal*) which isn't useful.
Similar, dynamic_cast can be used:
for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
if(Dog * dog = dynamic_cast<Dog*>(*it)) {
// it's a dog (or inherited from it). use the pointer
} else if(Cat * cat = dynamic_cast<Cat*>(*it)) {
// it's a cat (or inherited from it). use the pointer.
}
}
Note that in both cases, your Animal type should be polymorph. That means it must have or inherited at least one virtual function.
You can use dynamic_cast, as long as the vector contains Animal pointers.
vector <Animal *> stuff;
for(int i=0;i<stuff.size();i++) {
Dog *pDog = dynamic_cast <Dog *> (stuff[i]);
if(pDog) {
// do whatever with the dog
}
Cat *pCat = dynamic_cast <Cat *> (stuff[i]);
if(pCat) {
// and so on
}
}
but you should be aware that this is generally not the best practice. You should try to work with polymorphism, not against it. In other words, try to write a virtual Animal function that Dog and Cat override, and let the compiler automagically call the right one.
(Also, dynamic_cast is relatively slow, so too many of them will hinder performance; whereas a virtual function call is general just a single instruction.)
Are you sure you want to do that? What you're going to do is the exact contrary of polymorphism, and polymorphism is the best thing in object-oriented programming.
Loosely speaking: Don't do something if you animal is a Dog; let the Animal hierarchy know what to do when one of its objects is a Dog! :)
If you really need your application-level to identify Dogs vs non-Dogs, you should avoid using RTTI (dynamic_cast and typeid), and make that knowledge explicit in your class hierarchy.
for (size_t i = 0; i != v.size(); ++i) {
if (v[i]->isDog()) { v->cleanupPoop(); }
}
There are some minor performance benefits, but the primary benefit is exposing the necessary behavior in your class interface to maintenance programmers. RTTI (being as limited as it is) should not be required in order for a class hierarchy to function.
Now, along with what other people have said, it's likely that the isDog() function could be refactored into something that doesn't require knowledge of the entire hierarchy up-front (such as needsPoopCleanup()). Like everyone else said, you're losing the benefits of polymorphism if your application logic conditionally executes based on object type anyhow.
You can use the typeid operator to do this, e.g.
if (typeid(stuff[i].getClass())==typeid(Dog))
This can't catch if it's a derived class of Dog, though. You can use a dynamic_cast for that. However, any use of typeid or dynamic_cast is often indicative of a design flaw. Usually, you don't need to know what your derived types are, and there's probably a better way that involves polymorphism. It's hard to give the right advice without a real example, though.
using virtual functions:
As indicated by others responses, using virtual functions will often actually be sufficient enough, and is the "C++" way of thinking. Here is an example of using virtual functions:
#include<iostream>
#include<vector>
using namespace std;
/////////////
class Animal {
public:
virtual void move() { cout << "animal just moved" << endl; }
};
class Dog : public Animal {
public:
void move() { cout << "dog just moved" << endl; }
};
class Cat : public Animal {
public:
void move() { cout << "cat just moved" << endl; }
};
void doSomethingWithAnimal(Animal *a) {
a->move();
}
/////////////
int main() {
vector<Animal*> vec;
vector<Animal*>::iterator it;
Animal *a = new Animal;
Dog *d = new Dog;
Cat *c = new Cat;
vec.push_back(a);
vec.push_back(d);
vec.push_back(c);
it = vec.begin();
while( it != vec.end() ) {
doSomethingWithAnimal(*it);
it++;
}
return 0;
}
If this will not be sufficient, then others have already posted answers which actually use conditional logic instead of polymerized logic.
The accepted answer is correct, but you should know that there is another option as well that hasn't been mentioned. You could have a virtual function in the Animal class called "type()" which could return an int or a string (or any type that is comparable).
So for example:
class Animal {
/*...*/
public:
virtual std::string type() const { return "animal"; }
};
class Dog: public Animal{
/*...*/
public:
virtual std::string type() const { return "dog"; }
};
class Cat: public Animal{
/*...*/
public:
virtual std::string type() const { return "cat"; }
};
This way you could just do:
if(array[i]->type() == "dog") { }
The type function could return anything (an int unique to each derived type would work too, but strings illustrate it better).
Simply another option.