Reputation: 21
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
class ship
{
protected:
string type;
int size;
int hits;
};
int main()
{
map< char,vector<ship*> > board;
for(int i=0;i<10;i++)
{
vector<ship*> v;
for(int j = 0;j<10;j++)
{
ship *s = new ship();
v.push_back(s);
}
board['A'+i] = v;
}//set board
}
I made map<char,vector<ship*> >
board. How can I access to type
(in ship
class) from first ship pointer in first element of vector in map?
Upvotes: 0
Views: 1529
Reputation: 161
Ok, maybe my answer will be a bit over the top, and it's not really what you ask, I know, but it could help you understand why values in ship are protected, and I have too much time it seems.
Since it's a very common problem to build a game around ships/monsters/objects that have a common base of methods and attributes, there is a very common way to implement this. It's based on a three level inheritance.
1. Interface
It will define what methods can be called from the outside, it's a contract. Every class that inherits from it MUST implement those methods.
In your case a basic one would look like this:
class IShip
{
public:
virtual ~IShip() // needs this, see link below
{ std::cout << "Interface destructor called" << std::endl; }
virtual std::string type() = 0;
virtual int size() = 0;
virtual int hits() = 0;
virtual void specialMove() = 0;
};
Reason for the virtual dtor.
2. Abstract
It inherits from this interface, and implement the basic methods that are re-usable by every ship, it could be done this way:
class AShip : public IShip
{
public:
AShip(std::string type, int size, int hits)
: _type(type)
, _size(size)
, _hits(hits)
{ std::cout << "Abstract constructor called" << std::endl; }
virtual ~AShip() // still needs this
{ std::cout << "Abstract destructor called" << std::endl; }
virtual inline std::string type() // inline keyword to put the code in-place instead of calling it
{ return _type; } // should only do this on very small methods
virtual inline int size() // virtual are not necessary
{ return _size; } // but it's best practice to clarify code
virtual inline int hits()
{ return _hits; }
// -- need overload method
virtual void specialMove() = 0;
// -- protected since it needs to be accessible from child classes
protected:
std::string _type;
int _size;
int _hits;
};
At this point you can not instantiate anything because both those classes are virtual. More about virtual classes
An interface being a pure virtual class (because every methods defined have '= 0').
3. Instanciable
What you can do now is implement easily multiple classes that inherits from AShip but still specify something special, you are right, i'm talking about the specialMove, i'll create two classes for the sake of my example:
class TheDestructor : public AShip
{
public:
TheDestructor()
: AShip("The Destructor", 20, 100)
, _attack("DestructionOver9000")
{ std::cout << "TheDestructor constructor called" << std::endl; }
virtual ~TheDestructor() // still needs this to help the compiler
{ std::cout << "TheDestructor destructor called" << std::endl; }
inline void specialMove() // specialMove overload
{ std::cout << "Attack " << _attack << " from " << _type << std::endl; }
private:
std::string _attack;
};
class MyPunyShip : public AShip
{
public:
MyPunyShip()
: AShip("My Puny Ship", 1, 1)
, _escape("just fly away as fast as he can...")
{ std::cout << "MyPunyShip constructor called" << std::endl; }
virtual ~MyPunyShip() // still needs this to help the compiler
{ std::cout << "MyPunyShip destructor called" << std::endl; }
inline void specialMove() // specialMove overload
{ std::cout << _type << " " << _escape << std::endl; }
private:
std::string _escape;
};
Now let us test what has been done:
int main()
{
std::map<std::string, IShip*> ships;
ships.insert(std::make_pair("The Destructor", new TheDestructor));
std::cout << std::endl;
ships.insert(std::make_pair("My Puny Ship", new MyPunyShip));
std::cout << std::endl;
for (std::map<std::string, IShip*>::iterator itS = ships.begin() ; itS != ships.end() ; ++itS)
{
// *itS to access the data of the iterator
// second to access the second member of the pair
std::cout << "type: " << (*itS).second->type() << "\n";
std::cout << "size: " << (*itS).second->size() << "\n";
std::cout << "hits: " << (*itS).second->hits() << "\n";
std::cout << std::endl;
}
ships["The Destructor"]->specialMove();
ships["My Puny Ship"]->specialMove();
}
You can only call the methods that are in the interface, because the type in the map is an IShip, but it allows you to implement various ships with different stats.
let's see that output...
Output:
Abstract constructor called
TheDestructor constructor called
Abstract constructor called
MyPunyShip constructor called
type: My Puny Ship - size: 1 - hits: 1
type: The Destructor - size: 20 - hits: 100
Attack DestructionOver9000 from The Destructor
My Puny Ship just fly away as fast as he can...
But, but... Something is missing right? Something seems very odd... What??? I forgot to use delete??
Well, I forgot to use c++11 as a whole to make the example a bit smaller and to keep what I was trying to convey clear. What I should have used here is std::unique_ptr or std::shared_ptr.
The 'new' main would look like this and compile with c++11 flag enabled:
int main()
{
std::map<std::string, std::shared_ptr<IShip>> ships;
ships.emplace(std::make_pair("The Destructor", std::shared_ptr<IShip>(new TheDestructor)));
ships.emplace(std::make_pair("My Puny Ship", std::shared_ptr<IShip>(new MyPunyShip)));
for (auto ship : ships) // damn that's neat...
{
std::cout << "type: " << ship.second->type() << " - ";
std::cout << "size: " << ship.second->size() << " - ";
std::cout << "hits: " << ship.second->hits() << "\n";
}
ships["The Destructor"]->specialMove();
ships["My Puny Ship"]->specialMove();
}
Output:
Abstract constructor called
TheDestructor constructor called
Abstract constructor called
MyPunyShip constructor called
type: My Puny Ship - size: 1 - hits: 1
type: The Destructor - size: 20 - hits: 100
Attack DestructionOver9000 from The Destructor
My Puny Ship just fly away as fast as he can...
TheDestructor destructor called
Abstract destructor called
Interface destructor called
MyPunyShip destructor called
Abstract destructor called
Interface destructor called
Wow, I missed you so much c++11. Joke apart, you can see that the destructors are automatically called for our IShip* pointers, pretty nice if you ask me.
Why both Interface and Abstract, seems like overdoing it? My take on this is that one could need a IVehicle but create AShip, ACar, ASpaceCraft etc... with different limitations. But it's a very valid question, and you should adapt this "pattern" to fit your needs and your philosophy.
Hope it can help you understand a few concepts on c++/c++11 and inheritance, next step for you is to build a factory ;)
Upvotes: 1
Reputation:
First, you should declare type
as public
.
Then, you should do board[index].front()->type
Important: the index
should be a char
variable, since the type of the key of the map board
is char
.
example:
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
class ship
{
public:
string type;
int size;
int hits;
};
int main()
{
map< char, vector<ship*> > board;
for (int i = 0; i<10; i++)
{
vector<ship*> v;
for (int j = 0; j<10; j++)
{
ship *s = new ship();
s->type = "Cannon ship";
v.push_back(s);
}
board['A' + i] = v;
}//set board
cout << board['A'].front()->type << endl;
}
Upvotes: 0
Reputation: 1898
Since type
is declared as protected
, you can't normally access that. protected
members are only accessible by friend
classes or those who inherit from the class. The easiest solution is to declare the variables as public
. But it has its own caveats.
Upvotes: 0