访问一个我只想存在于派生类中的函数

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

本文关键字:派生 于派生 函数 只想 一个 访问 存在      更新时间:2023-10-16

我有一个基类base和几个派生类:Derived1、Derived2和Derived3。

我希望在所有这些中都有Function funcOne,这样我就可以定期访问它,如下所示:

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

但我希望在Derived1类中只有函数funcTwo。问题是,我想这样访问它:

d1->funcTwo();

除了用某种实现(例如)在基类中创建虚拟funcTwo之外,还有可能用其他方式来实现吗

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

其他实现只针对Derived1类?

谢谢!

我能想到两个主要选项。

您可以在基类中实现一个虚拟funcTwo,如您的问题所述。这通常是不好的做法,因为funcTwo在Base、Derived2和Derived3的上下文中可能没有意义。如果API的误用没有编译,而不是抛出错误,或者更糟的是,无声地失败,这会更好。这看起来像:

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

或者,您可以在Derived1中实现funcTwo。然后,如果可能的话,可以尝试直接保持指向Derived1的指针,如果不可能的话可以使用dynamic_cast。这是最好的,因为对Base::funcTwo的直接调用将无法编译,并且可以使语义更接近实际要表达的内容。

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

除了在基类中创建一个虚拟funcTwo之外,是否可以用其他方式来完成它〔…〕

如果您想将所有指针统一处理为Base*类型,则不需要。

也就是说,你可以这样做:

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();

你也可以动态抛出你的指针(但这是一个比我上面的代码更糟糕的解决方案):

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

客户代码:

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

是的,您有用于此目的的dynamic_cast。如果您的Base指针是Derived1,则强制转换将起作用,然后您可以调用funcTwo()。如果不是这样,演员阵容就会失败。

在大多数情况下,使用dynamic_cast是一个设计缺陷。它对某些事情很有用,比如组件加载和版本控制,因为您不知道正在加载组件的哪个版本,所以您可以检查它是否支持更新的功能。

就你而言,这可能是错误的。

不管怎样,如果你必须这么做的话。

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

如果您根本不希望成员函数在Base中,您可以使用static_cast:向下转换指向具有该函数的正确类的指针

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

当然,这需要指针b实际上是指向Derived实例的指针,或者您有未定义的行为。

注意:有这样的下转换,并根据某个东西的类型做不同的事情,通常被认为是糟糕的设计。

我认为最好的办法是使用静态方法来尝试为您进行解析,并执行仅存在于派生类中的函数。

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);
}

这种方法将花费dynamic_cast,但为您节省一个vtable查找,还将为您提供所需的行为。