Matt Capone
Matt Capone

Reputation: 157

Making subclasses that can call on each other's shared member variables

I'm new to C++ and I'm having trouble with this. So I'm trying to make classes that can call on each other's fields using a generic subclass.

Basically, I'm trying to make a game where there is a generic type and three types that have strengths/weaknesses against each other. I only really have experience in Java, and the translation to c++ for this kind of thing isn't clicking for me

Generic type splits into three types: type 1, type 2, type 3.

Generic type needs to be concrete

In the generic type there are fields attack, defense, hitpoints

types 1, 2, and 3 all inherit these fields.

I'm trying to make a virtual function in the generic class:

virtual void attack(Generic* g);

Problem is, when I try to make type_1.attack(generic) for example, I want to do g->hitpoints to get generic's hitpoints, but it just doesn't seem to work like that in C++.

Additionally, I know I must be doing something wrong, because type 1, 2 and 3 all include the generic type's header, but if I want to include those 3 headers in main.cpp, it'll give me an error for declaring generic 3 separate times.

How would I go about doing this?

Sorry this is a very specific question and it's a little bit vague. Please let me know if I need to clarify anything

Edit: Here is the basic setup of what I'm talking about

//in generic.h

class Generic {
protected:
    int hitpoints;
    int strength;
    int defense;
public:
    virtual void attack(Generic* g);
};

//type1.h
#include 'generic.h'
class Type1 : public Generic {
    void attack (Generic* g);
};

//in type1.cpp
void Type1::attack(Generic*g) {
    g->hitpoints = strength - g->defense;
}

Upvotes: 1

Views: 1151

Answers (5)

Levin Fritz
Levin Fritz

Reputation: 11

The meaning of protected for data members is subtly different from what you might expect. When you have a class A with a protected data member d and a class B that inherits from A (with public inheritance), then B's member functions can access d – but only on objects of type B, not on any object of type A. If that seems confusing, a code example will hopefully make it clearer:

class A {
protected:
    int d;
};

class B : public A {
    void fine(B& b)  { b.d = 0; }
    void wrong(A& a) { a.d = 0; }
};

int main() { }

As the names indicate, the assignment in functions fine is ok, but you'll get a compiler error for the one in wrong if you try to compile the code. The best way to deal with this is probably to make the data members private and write protected member functions that operate on them, as in abyss.7's answer.

For the double inclusion problem, use either include guards or #pragma once.

Upvotes: 1

Gorpik
Gorpik

Reputation: 11028

To avoid your second problem (a class defined multiple times), there is something called include guards (they guard you from including the same file several times). It works like this way:

// File: type1.h
// At the very beginning
#ifndef type1_h
#define type1_h

// Here come the rest of your file and, at the end:

#endif

This way, the contents of the file are only included once, because after that type1_h will be defined, so everything will be skipped. This may be the only use of #define that is universally accepted in C++.

As for your first problem, protected means that derived classes can read that member for themselves or objects of their own class, but nobody else. This includes that derived classes cannot read that member for objects belonging to another class, including the base class itself. I'm afraid you'll have to rethink your design.

The best that you can do is just changing the privacy of the protected members or, even better, provide public accessors and keep the data members private. public data members are seldom a good idea in classes (structs are a different thing):

class Generic {
private:
    int hitpoints;
    int strength;
    int defense;
public:
    virtual void attack(Generic* g);

    void SetHitpoints(int hp) {hitpoints = hp;}
    int GetDefense() {return defense;}
};

Upvotes: 1

abyss.7
abyss.7

Reputation: 14462

You should do something like this:

// generic.h
#pragma once

class Generic {
  protected:
    int hitpoints_;
    int strength_;
    int defense_;

    void do_damage(Generic* g, int damage) { g->hitpoints_ -= damage; }
  public:
    virtual void attack(Generic* g) = 0;

    int hitpoints() const { return hitpoints_; }
    int strength() const { return strength_; }
    int defense() const { return defense_; }
 };

 // type1.cpp
 void Type1::attack(Generic* g) {
   do_damage(g, strength_ - g->defense());
 }

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 596256

An object can access its own protected members that are inherited from a base class. But an object cannot access another object's protected members. That is the difference between private vs protected - private members can only be accessed by the class that declares them, but protected members can be accessed by the declaring class and its descendants. Both are still private to outside code, though.

To do what you are attempting, hitpoints needs to be public.

Upvotes: 3

mort
mort

Reputation: 13588

type 1, 2 and 3 all include the generic type's header, but if I want to include those 3 headers in main.cpp, it'll give me an error for declaring generic 3 separate times.'

You need to make sure that the class is declared only once. This is usually done with guards in the .h file:

//generic.h
#ifndef GENERIC_H
#define GENERIC_H

//all declarations go here

#endif /* GENERIC_H */

When you type #include "generic.h", the C++ processor will basically just paste the contents of foo.h. Since you included it thrice (via the includes for classes Type1, Type2 and Type3, that each include generic.h), the class is delcared three times.

Upvotes: 1

Related Questions