Skip to main content
GameDev.net gamedev.net
🔒 Locked

c++ Derived/Abstract class question

Started by johnnyBravo Oct 30, 2004 at 3:28 AM 12 replies 2.3k views
Original Post
johnnyBravo
johnnyBravo
Hi, with classes in c++, I am curious if its possible to do classes like this for example:

class Base  {
protected:
     void increaseLight();
};

class Light: public Base {
private;
    int brightness;
};

void Light::increaseLight() {
    brightness++; // I get a can't access private error here
}

I am wondering if its possible to make 'increaseLight()' be able to access the private brigtness class? Thankyou
snk_kid
snk_kid
you need to use dynamic binding, make increase_light a virtual or pure virtual member function.

#include <iostream>struct base {    virtual void increase_light() = 0;    virtual ~base() {} //rule of thumb if a class has a virtual function make the destructor virtual aswell.};class light : public base {   int brightness;public:   light(int i = 0): base(), brightness(i) {}   void increase_light() {       ++brightness;   }   int get_brightness() const { return brightness; }};int main() {   light l(30);   base* b = &l   b->increase_light();   std::cout << l.get_brightness() << std::endl;}
kuphryn
kuphryn
In general, private member is restricted to only that class. No classeses including the base can access the private member.

Kuphryn
Mayrel
Mayrel
class Base  {protected:     void increaseLight();};class Light: public Base {private:    int brightness;};void Light::increaseLight() {    brightness++; // I get a can't access private error here}

This doesn't work, but you don't get the error you say you get. There's no such function as void Light::increaseLight().

To determine what the most elegant solution to your problem is, you need to describe your problem more accurately.
Quote:
Original post by snk_kid
you need to use dynamic binding, make increase_light a virtual or pure virtual member function.

Well, no. You don't need to do that. Other options are making the field protected rather than private, or making Light a friend of Base.
snk_kid
snk_kid
Quote:
Original post by Mayrel
Well, no. You don't need to do that. Other options are making the field protected rather than private,


making brightness protected in light makes no difference.

Quote:
Original post by Mayrel
or making Light a friend of Base.


that is just ugly.

MaulingMonkey
MaulingMonkey
It's quite possible, it's just that you've got an error in your class definition. Here's a rewrite of your code:

class Base {protected:    void increaseLight(); //defines void Base::increaseLight()};class Light : public Base {private:    int brightness;protected:    void increaseLight(); //defines void Light::increaseLight(), which overrides Base::increaseLight()};void Light::increaseLight() {    ++brightness; //you should prefer to pre-increment when possible, it can be faster (with STL iterators - but it's good to get in the habit even with variables)}


Note: Base::increaseLight() has never been defined, you will get a link error if you try to link just this code. In this case, you probably want increaseLight to be a pure virtual function in the base class, so you'd change the definition of Base to:

class Base {protected:     virtual void increaseLight() = 0;};


Note: now we can't create an instance of base. We can create an instance of Light because it has no pure virtual functions (it supplies functions for all the virtual ones) however.
Mayrel
Mayrel
Quote:
Original post by snk_kid
Quote:
Original post by Mayrel
Well, no. You don't need to do that. Other options are making the field protected rather than private,

making brightness protected in light makes no difference.

Well, no, not in the example given. But that's because the problem in the example given isn't that the function tries to access a private member. It's that the function doesn't even exist.

It should be obvious that johnnyBravo put the bodies of the Base and Light class the wrong way around. When they are swapped, the function does try to access an inaccessible field, as he said. In that case, the problem is solved by making that field protected.
Quote:

Quote:
Original post by Mayrel
or making Light a friend of Base.

that is just ugly.

Given that you don't know what the actual problem that johnnyBravo's trying to solve is, how can you be so sure that using a friend is automatically the wrong solution?
Qw3r7yU10p!
Qw3r7yU10p!
Quote:
Original post by Mayrel
the problem is solved by making that field protected.


Protected member data is almost certainly not the right solution.

Member data is private for a reason: you don't know that clients will be able to modify it correctly.

Making it protected says 'I don't trust people to modify this correctly, unless they derived from this class', which just doesn't make sense. Why should the act of someone deriving from your class make them any wiser about maintaining a member variable?

Having a protected member variable means that clients of the class will depend upon it, even if they can't access it.

See some quotes from Bjarne Stroustrup original designer of the language.

If you are wanting to hide data it is often good to make it private and behind an opaque pointer:

//The base implementation - will be hidden//LightImpl.hclass LightImpl {public:    virtual DoIncreaseLight() {};};

//Light.hclass LightImpl;//the base implementationclass Light {private:    LightImpl* m_pImpl;public:    Light(LightImpl* pImpl) : m_pImpl(pImpl) {    }    void IncreaseLight();};


//Light.cpp#include "Light.h"#include "LightImpl.h"void Light::IncreaseLight() {    m_pImpl->DoIncreaseLight();}

//MyLight.h#include "Light.h"class MyLight : public Light {public:    MyLight();};

//MyLight.cpp#include "MyLight.h"#include "LightImpl.h"class MyLightImpl : public LightImpl {private:    int m_brightness;public:    MyLightImpl() : m_brightness(0) {    }    void DoIncreaseLight() {        ++m_brightness;    }};MyLight::MyLight() : Light(new MyLightImpl()) {}


So clients of your class would include MyLight.h, which would include Light.h and that would be it. No exposure to implementation details.

#include "MyLight.h"int main() {    Light* light = new MyLight;    light->IncreaseLight();    delete light;    return 0;}

As you can see, if you want to derive from the class you create a new class derived from LightImpl, which is where all the work will be done. You also derive another class from Light and initialise it with your new LightImpl. So you have two classes instead of one. Such is the cost of insulation. One of them is trivially easy to implement though.

For some further reading see Herb Sutter's Virtuality on Guru of the Week.

He suggests making virtuals private by default and only protected if necessary. This is for a design where they are all kept in the one class but I have broken them into separate classes. Maybe you could make the virtual functions private and then make Light a friend of LightImpl. I didn't want to confuse things (any further) in my example but that is probably what I would do in real life.

[Edited by - petewood on October 30, 2004 11:13:45 PM]
mumpo
mumpo
class Base {
protected:
virtual void increaseLight() = 0; // made the function pure virtual so that it could be defined in derived classes and does not need to be defined in the base class
};

class Light: public Base {
private: // changed ";" to ":"
int brightness;
virtual void increaseLight(); // added a definition of increaseLight() so that it is actually a member of Light; the virtual keyword is not necessary here but makes the code more readable
};

void Light::increaseLight() {
brightness++; // there should be no error here now
}
snk_kid
snk_kid
Quote:
Original post by petewood
Quote:
Original post by Mayrel
the problem is solved by making that field protected.


Protected member data is almost certainly not the right solution.


i would agree, its a complete contridication for making the representation encapsulated in the first place, you make a data members private to enforce state invariants & provide loose coupling among related/un-types & modules but then when you make a data member protected you've just violated those prinicples by allowing clients to access through extending the class this makes tight coupling & the possiablity of putting the instances in an invalid state because the state invariant can not be maintained anymore.

This is the problem people seem to think that type inheritance is about extending data/state, its not its about extending behaviours/operations of a type, type inheritance is not the wright tool for data/state inheritance its to inflexible type inheritance is usually a static relationship in a programming language where as data/state inheritance needs to be a dynamic relationships, frequently changing, also when using type inerhtiance as/for data/state inheritance you'll end up with casting syndrome all over your code.

There are much better ways to do data/state inheritance and it doesn't have to involve type inheritance one bit.

[Edited by - snk_kid on October 31, 2004 3:30:46 AM]
Mayrel
Mayrel
Quote:
Original post by petewood
Quote:
Original post by Mayrel
the problem is solved by making that field protected.

Protected member data is almost certainly not the right solution.

I didn't say it was necessarily the right solution, just that it was a solution. Having though about it, I agree with your conclusion, but not with your reasoning.

Although public/protected data members may allow an invariant to be violated, it is often possible that any value of the data member is valid or that the data member is smart and can maintain its own invariant. So although invariants are an issue, they are not a blanket excuse to prohibit public/protected data.

The actual problem with public/protected data members is not that they break the class, but that they may break the client if the class is changed. Public/protected data members make it impossible to change the representation of the class's state without breaking clients.
Quote:

As you can see, if you want to derive from the class you create a new class derived from LightImpl, which is where all the work will be done. You also derive another class from Light and initialise it with your new LightImpl. So you have two classes instead of one. Such is the cost of insulation. One of them is trivially easy to implement though.

In general, the advantages of this pattern outweigh the drawbacks only when LightImpl's interface is less stable than Light's interface.
Quote:
Original post by snk_kid
This is the problem. People seem to think that type inheritance is about extending data/state. It's not: it's about extending behaviours/operations of a type. Type inheritance is not the right tool for data/state inheritance; it's too inflexible.

That's too bad because, excepting template specialisations, there's no way to inherit from a type without inheriting its data/state.
snk_kid
snk_kid
Quote:
Original post by Mayrel
Quote:
Original post by snk_kid
This is the problem. People seem to think that type inheritance is about extending data/state. It's not: it's about extending behaviours/operations of a type. Type inheritance is not the right tool for data/state inheritance; it's too inflexible.

That's too bad because, excepting template specialisations, there's no way to inherit from a type without inheriting its data/state.


obviously you don't understand and/or mis-understood, with type inheritance the emphasis is on extending behaviours, just as unrelated types & modules should not care how a type is representated nor should related sub-types so protected data members are sign of bad design & is contradictive.

if you really believe its about extending state your designs are going to brittle & tightly coupled, there are explicit designs that deal with state inheritance and the kind of inheritance that c++ support in the language is not that kind.
Mayrel
Mayrel
It's not an question of design or what you're emphasising. The simple fact is that you cannot inherit from a class without inheriting its state.
snk_kid
snk_kid
Quote:
Original post by Mayrel
It's not an question of design or what you're emphasising. The simple fact is that you cannot inherit from a class without inheriting its state.


[headshake] no you don't inherit state there is no state to inherit from because there is no state involved in type inheritance, only instances/objects have real state not normally types.

type inheritance != state inhertiance, type inheritance is usually a static relationship between types and is the kind of inheritance supported in OO programming languages directly like c++, object/data hierarchies usually involve pointers/references to instances of some type.

i'm gonna have to show you a real-life example of the differences between a type hierarchy & a data hierarchy because you don't understand what state inheritance is, using a a typical scene graph where the former implies behavioural inheritance and the latter implies state inheritance.

type hierarchy of a typical scene graph



and an instance of that scene graph, data hierarchy:



like to see how you would model that in a type hierarchy.

Anyways lets just agree to disagree, instead of high-jacking some-one elses thread.

[Edited by - snk_kid on November 1, 2004 11:58:06 AM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.