我对C ++很新,这就是我遇到的问题:
我有两个班级,Client
和Host
。当所有内容都加载后,您可以选择按两个按钮,如果按下按钮1 Client
已加载,如果按下按钮2 Host
已加载。
现在Client
和Host
都是相当大的类,我不想将它们都放入内存中。所以我的想法是创建一个Base
类,然后Client
和Host
都应该扩展基类,然后我唯一需要做的就是:
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 ++知道我正在与班级Client
或Host
而不是Base
交谈?我也对这个问题有了全新的解决方法。也许这只是我思考过程中的一个错误。
提前致谢!
答案 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
中的函数定义。A
和B
,但您可以声明一个函数,我们将其称为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
的状态Client
。 connection
仍然是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();
关于演员部分,我会说,如果你发现自己必须演绎到一个孩子类型,你应该重新考虑课程的设计。