argamanza
argamanza

Reputation: 1142

Accessing a function that i want to exist only in a derived class

I have a base class Base, and few derived classes: Derived1, Derived2 and Derived3.

I would like to have Function funcOne in all of them so i could regularly access it like this:

Base* d1 = new Derived1();
Base* d2 = new Derived2();
Base* d3 = new Derived3();

d1->funcOne();
d2->funcOne();
d3->funcOne();

but i would like to have Function funcTwo ONLY in Derived1 class. The issue is, i want to access it like that:

d1->funcTwo();

Is it possible to do it in some way other than creating a virtual funcTwo in the base class with an implementation of some kind, for example

void funcTwo(){ cout << "There is no funcTwo for this class" << endl; }

And other implementation only for the Derived1 class?

Thanks!

Upvotes: 3

Views: 403

Answers (5)

Andrew
Andrew

Reputation: 643

I think your best bet is to use a static method to try to do the resolution for you and execute functions that exist only in your derived classes.

class Base {
public:
  virtual ~Base() {}
  virtual void func1() = 0;
};

class Derived1 : public Base {
public:
  virtual ~Derived1() {}
  virtual void func1() { std::cout << "Derived1::func1" << std::endl; }
  void func2() { std::cout << "Derived1::func2" << std::endl; }
};

class Derived2 : public Base {
public:
  virtual ~Derived2() {}
  virtual void func1() { std::cout << "Derived2::func1" << std::endl; }
};

void func2(Base& b) {
  Derived1* d = dynamic_cast<Derived1*>(&b);
  if (d) d->func2();
}


void f() {
  Base* b1 = new Derived1;
  Base* b2 = new Derived2;

  func2(*b1);
  func2(*b2);
}

This approach will cost you a dynamic_cast, but save you a vtable lookup and will also provide you the desired behavior you were looking for.

Upvotes: -1

utnapistim
utnapistim

Reputation: 27365

Is it possible to do it in some way other than creating a virtual funcTwo in the base class [...]

Not if you want to treat all pointers uniformly, as Base* type.

That is, you could do this:

Derived1 *d1 = new Derived1(); // not treated as a base*
Base* b1 = d1;
Base* b2 = new Derived2();
Base* b3 = new Derived3();

d1->funcTwo(); // not treated as a base*
b1->funcOne();
b2->funcOne();
b3->funcOne();

You could also dynamic cast your pointers (but that's a worse solution than my code above):

void CallFunc2(Base* b) {
    if(auto d = dynamic_cast<Derived1*>(b))
        d->funcTwo();
}

Client code:

Base* b1 = new Derived1();
Base* b2 = new Derived2();
Base* b3 = new Derived3();
CallFunc2(b1); // will call Derived1::funcTwo
CallFunc2(b2); // will do nothing
CallFunc2(b3); // will do nothing

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409166

If you don't want the member function to be in Base at all, you could downcast the pointer to the correct class that have the function, using static_cast:

Base* b = new Derived;
static_cast<Derived*>(b)->func2();  // Call function only available in `Derived`

Of course, this requires that the pointer b is really a pointer to a Derived instance, or you have undefined behavior.

Note: Having downcasts like this, and doing different thing depending on the type of something, is usually considered bad design.

Upvotes: 0

TartanLlama
TartanLlama

Reputation: 65610

You have two main options I can think of.

You could implement a virtual funcTwo in the base class as outlined in your question. This is generally bad practice as presumably funcTwo does not make sense in the context of Base, Derived2 and Derived3. It is better if a misuse of the API does not compile rather than just throw an error or, worse, silently fail. This looks like:

class Base() {virtual void funcTwo() {throw runtime_error("This should not be called");};
Base *d1 = new Derived1;
Base *d2 = new Derived2;
d1->funcTwo(); //fine
d2->funcTwo(); //compiles even though it doesn't make semantic sense, throws an exception

Alternatively, you could just implement funcTwo in Derived1. You would then try and hold pointers directly to Derived1 when possible and use dynamic_cast when not possible. This is preferable as direct calls to Base::funcTwo would fail to compile and you get semantics closer to what you are actually trying to express.

Base *b = new Derived1;
Derived1 *d1 = new Derived1;
Base *d2 = new Derived2;
d1->funcTwo(); //fine
if ((d1 = dynamic_cast<Derived1*>(b)) d1->funcTwo(); //fine
if ((d1 = dynamic_cast<Derived1*>(d2)) d1->funcTwo(); //funcTwo is not called, no errors
b->funcTwo(); //fails to compile

Upvotes: 4

CashCow
CashCow

Reputation: 31435

Yes, you have dynamic_cast for that purpose. If your Base pointer is a Derived1 the cast will work and then you can call funcTwo(). If it isn't the cast will fail.

Using dynamic_cast is a design flaw most of the time. It is useful for some things like component loading and versioning where you don't know which version of a component is being loaded so you check to see whether it supports newer functionality.

In your case it is probably wrong.

Anyway if you do have to do it.

Base * b = getBasePtr(); // I don't know which one I got
Derived1 * d1 = dynamic_cast< Derived1 * >( b );
if( d1 )
    d1->func2();

Upvotes: 0

Related Questions