C ++:将父类强制转换为子类

时间:2013-01-23 23:40:13

标签: c++ oop class inheritance casting

我对C ++很新,这就是我遇到的问题:  我有两个班级,ClientHost。当所有内容都加载后,您可以选择按两个按钮,如果按下按钮1 Client已加载,如果按下按钮2 Host已加载。

现在ClientHost都是相当大的类,我不想将它们都放入内存中。所以我的想法是创建一个Base类,然后ClientHost都应该扩展基类,然后我唯一需要做的就是:

Base connection;

//If button 1 is pressed:
connection = Client();

//If button 2 is pressed:
connection = Host();

这听起来好得令人难以置信,当我尝试它时,我没有错误。现在出现问题,Base有一个名为A的函数,Client有一个名为B的函数。因此,函数B对于类Client是唯一的。

当我尝试调用函数B时,我收到此错误:'class Base' has no member named 'B'。我怎样才能让C ++知道我正在与班级ClientHost而不是Base交谈?我也对这个问题有了全新的解决方法。也许这只是我思考过程中的一个错误。

提前致谢!

4 个答案:

答案 0 :(得分:6)

您遇到了我们称之为对象切片的情况,这是使用值语义(如C ++)的语言中的常见问题。

当您将子类型的值分配给超类型的新位置(您的变量connection)时,会发生对象切片。这引入了实例的新副本,但是超类型而不是子类型,因此您将丢失有关要实例化的具体类的信息。

为避免这种情况,您有多种选择。

经典方法使用指针

Base * connection;
connection = new YourConcreteType();

然后,要使用此对象,您必须使用星号运算符(* derefrerence

(*connection).function();
connection->function();    // syntactic sugar, semantically equivalent

不要忘记:使用后您必须删除对象:

delete connection;

为了简化这一过程,C ++引入了两个概念:引用智能指针。虽然前者有一个限制只被分配一次,但它是语法上最简单的一个。后者类似于指针方法,但您不必关心删除,因此您不太可能遇到内存泄漏情况:

std::shared_ptr<Base> connection;

connection = make_shared<YourConcreteType>(); // construction via 'make_shared'

// ... use as if it were just a pointer ...

connection->function();

// no delete!

还有其他“智能指针”类型,如unique_ptr,如果您不打算传递指针(如果它在范围内),则可以使用它。< / p>

现在,您可以分别在两个类中实现这些功能。为了利用多态,这意味着,在运行时,要么调用一个子类的功能,要么调用另一个子类的功能,这取决于构造的是哪一个,你应该将基类中的函数声明为virtual否则,无论您构造的具体类型如何,都将调用Base中的函数定义。

在您的情况下,您希望调用一个应该执行不同操作的函数,具体取决于类型。虽然您的方法是引入两个不同的函数,即AB,但您可以声明一个函数,我们将其称为handleEvent,作为纯虚拟

Base {
    ....
    virtual void handleEvent(...) = 0; // "= 0" means "pure"
};

// Don't provide an implementation

Client {
    void handleEvent(...); // override it
};

// define it for Client:
void Client::handleEvent(...) {
    ...
}

Host {
    void handleEvent(...); // override it
};

// define it for Host:
void Host::handleEvent(...) {
    ...
}

答案 1 :(得分:3)

执行此操作时:

Base connection;

您创建的对象connection具有类型Base所需的内存。

执行此操作时:

connection = Client();

您没有扩展分配的内存或将connection转换为Client实例。相反,您正在创建一个更大的对象,将其“切成”为较小的对象。

你想要做的是使用指针(或引用或智能指针),这样你保留的只是一个对象的地址,可能是一种类型或另一种类型。 像这样:

Base *connection;
...
connection = new Client();

第一个语句创建一个指向Base类型对象的指针,第二个语句为Client类型的对象分配内存,初始化它并将其地址分配给connection。< / p>

答案 2 :(得分:2)

如果你想使用Base,那么你将不得不这样做来使用这些功能:

if (Button1) {
    dynamic_cast<Client*>(connection)->A();
} else {
    dynamic_cast<Host*>(connection)->B();   
}  

你需要连接一个指针。 Base * connection

但这并不是很理想。你应该像在其他答案中一样研究一种不同的方式。

答案 3 :(得分:2)

首先,这一行

connection = Client();

正在使用赋值运算符从临时connection对象设置Base的状态Clientconnection仍然是Base个对象。你能做的是以下几点:

std::unique_ptr<Base> makeBase(someFlagType flag)
{
  if (flag) {
    return std::unique_ptr<Base>(new Cient);
  } else {
    return std::unique_ptr<Base>(new Host);
  }
}

然后

std::unique_ptr<Base> b = makeBase(myFlag);
b->someBaseMethod();

关于演员部分,我会说,如果你发现自己必须演绎到一个孩子类型,你应该重新考虑课程的设计。