views:

94

answers:

2

I have a simple C++ base class, derived class example.

// Base.hpp 
#pragma once

class Base
{
public:
    virtual float getData();
    virtual void setData(float a, float b);
    virtual void setData(float d);

protected:
    float data;
};

//Base.cpp
#include "stdafx.h"
#include "Base.hpp"

float Base::getData()
{ 
    return data; 
}

void Base::setData(float a, float b)
{ 
    setData(a);
}

void Base::setData(float d)
{ 
    data = d;
}

//Derived.hpp
#pragma once
#include "Base.hpp"

class Derived
    : public Base
{
public:
    virtual void setData(float d);
};

//Derived.cpp
#include "stdafx.h"
#include "Derived.hpp"

void Derived::setData(float d)
{
    data = d + 10.0f;
}

If I now make a pointer to the Base this compiles fine.

//Main.cpp
#include "stdafx.h"
#include "Base.hpp"
#include "Derived.hpp"

Base *obj = new Derived();

But if I make a pointer to the Derived class, then the compiler (VC 2008 and 2010) complains that:

Main.cpp(12): error C2660: 'Derived::setData' : function does not take 2 arguments

And here is the code that causes this error:

//Main.cpp
#include "stdafx.h"
#include "Base.hpp"
#include "Derived.hpp"

Derived *obj = new Derived();

It seems that the base class methods are being hidden. I was under the impression that since the base class methods are virtual they should be visible even when viewed from the Derived pointer, or am I wrong?

+5  A: 

This is an artifact of C++ name lookup. The basic algorithm is the compiler will start at the type of the current value and proceed up the hierarchy until it finds a member on the type which has the target name. It will then do overload resolution on only the members of that type with the given name. It does not consider members of the same name on parent types.

The way to work around this is to redefine the functions on Derived and just forward them up to Base

class Derived {
  ...
  void setData(float a, float b);
}

void Derived::setData(float a, float b) {
  Base::setData(a,b);
}

Additionally you can bring the base members into scope using the using declaration

class Derived {
  using Base::setData;
  ...
}

Documentation on this use of using

JaredPar
Maybe you could add a hint to `using`?
sbi
@sbi, good call. Added
JaredPar
@Jerry, thanks fixed
JaredPar
Good clear answer @JaredPar thanks.
Brian Heylin
+1  A: 

A function in a derived class will hide any function of the same name in a base class.

If you really want the base class function(s) of that name to be visible, you can add a using declaration to make it happen:

class Base { 
public:
    void setData(float d);
    void setData(float d, float e);
};

class Derived : public Base { 

    using Base::setData;

    void SetData(float d);
};

[Revised:] Seems my memory was wrong. This doesn't cause an ambiguity -- even with the using declaration, Derived::setData takes precedence (so to speak) so when you invoke setData(float) on a derived object, you get Derived::setData(float). If you make it virtual and invoke via a pointer to Base, you still get Derived::setData(float);.

Jerry Coffin
Yup, this is the way to go, `+1` from me (although Jared explained the reasons more thoroughly).
sbi