首先,我不确定如何准确地用一行来描述我在做什么...因此标题有点模糊。
我能给出的问题的最短描述是“我有一个函数,它应该能够将许多可能的类类型中的任何一个作为参数,这些类都是从基类派生的”。
具体来说,我有两个类别的类,它们都实现了不同类型的方法,它们很相似,但不完全相同。
也许我只是举个例子会更好?你会看到我用指针类型转换做了一些稍微奇怪的事情。我不认为这些是好的编程实践。它们至少有点奇怪,我想知道是否有替代的、更好的做事方法。
好的,下面是我尝试的一个简化示例:
class device
{
// Nothing here - abstract base class
}
class inputDevice : device // inherit publicly, but it doesn't matter
{
virtual input* getInput() { return m_input; } // input is a class
}
class outputDevice : device
{
virtual output* getOutput() { return m_output; } // output is also a class
}
class inputoutputDevice : public inputDevice, public outputDevice
{
// Inherits the get methods from input and output types
}
// elsewhere in program
void do_something(device* dev, int mode_flag)
{
if(mode_flag == 1) // just an example
{
input* = ((inputDevice*)dev)->getInput(); // doing strange things with pointers
}
else if(mode_flag == 2)
{
output* = ((outputDevice*)dev)->getOutput(); // strange things with pointers
}
else if(mode_flag == 3)
{
}
}
所以你看到这里的微妙之处在于,该函数具有一些行为,这取决于我们是在处理一个参数,即输入设备还是输出设备。
我想我可以多次重载函数,但可能有许多不同类型的输入,输出或输入和输出设备...因此,这将是一个相当复杂的方法。
将“get”方法放入基类似乎也不是一个好主意,因为如果设备是输出设备,派生类就不应该有< code>getInput()方法。同样,输入设备也不应该有< code>getOutput()方法。从概念上讲,这似乎并不正确。
我希望我解释得足够清楚,没有犯任何错误。
由于问题领域相当广泛,不可能给出精确的答案,但是由于它提到了设备,linux内核设备模型可能是合适的。
请参阅linux-core Tag wiki以深入了解。在那里查看LDD3,因为它是一本免费的电子书,您可以查看内核内部的工作原理。
linux内核的一般概念是每个设备都由文件表示。因此,您的驱动程序导出具有vtable的文件描述符(参见fs. h)。
最简单的字符设备之一是命名管道(见其vtable,文件中还有所有函数定义)。
一个简单的 C 转换可能如下所示:
struct abstract_dev {
virtual int read(input *) { return -1; /* fail */ }
virtual int write(output *) { return -1; /* fail */ }
virtual int ioctl(int cmd, void **args) { return -1; }
};
struct input_dev : public abstract_dev {
input_dev() : state(0) {}
int state;
int read(input *) override {
if (state != 2) {
return -1;
}
/* do smth */
return 0;
}
int ioctl(int cmd, void **args) override {
if (cmd == 2) { state = 2; return 0;}
return -1;
}
};
对于模式,内核使用ioctl
系统调用来设置模式(作为控制面)并将状态保存在设备驱动程序中。随后的读取和写入会考虑到模式。在命名管道示例中,您可以通过设置FIONREAD
值来更改内部缓冲区大小。
我希望这有助于解决你的问题。
函数<code>do_something()
首先,当您期望您的设备
类是多态的时,您应该预见到一个虚拟析构函数。这将确保设备也是多态的。
然后,您可以利用动态转换来使您的代码可靠(这里我假设mdode 3是用于输入/输出的,但这只是一般的想法):
void do_something(device* dev, int mode_flag)
{
if(mode_flag == 1 || mode_flag==3) // just an example
{
inputDevice* id=dynamic_cast<inputDevice*>(dev); // NULL if it's not an input device
if (id) {
input* in = id->getInput(); // doing strange things with pointers
}
else cout << "Invalid input mode for device"<<endl;
}
if(mode_flag == 2 || mode_flag==3)
{
outputDevice* od=dynamic_cast<outputDevice*>(dev);
if (od) {
output* out = od->getOutput();
}
else cout << "Invalid output mode for device"<<endl;
}
// ...
}
我不知道它有多复杂,但如果你打算用任何类型的设备做一些事情,你可以把它变成一种方法。
class device {
public:
virtual void do_something(int mode_flag) = 0;
virtual ~device() {}
};
你会明白的。当然,你也可以有一个组合,它有一个全局do_something()
函数来执行一般步骤,并为应该取决于设备类型的部分调用成员函数。
请注意,您的<code>inputoutputDevice<code>从设备继承了两次。一旦设备中有了一些成员,这可能会导致歧义。因此,我建议您考虑设备类的虚拟继承。
class inputDevice : public virtual device
...;
class outputDevice : public virtual device
...;
另一种方法可能是在设备中具有更复杂的输入/输出接口:
class device {
public:
virtual bool can_input() = 0; // what can the device do ?
virtual bool can_output() = 0;
virtual input* getInput() = 0;
virtual void setOutput(output*) = 0;
virtual ~device() {};
};
class inputDevice : public virtual device {
bool can_input() { return true; }
bool can_output() { return false; }
input* getInput() { return m_input; } // input is a class
void setOutput(output*) { throw 1; } // should never be called !
};
...
void do_something(device* dev, int mode_flag)
{
if(mode_flag == 1 && dev->can_input() ) // just an example
...
...
}
为了扩展我的评论,如果您查看例如这个输入/输出库参考,您将看到一个类图,它在某种程度上让您想起了您的类层次结构:有一个基类(实际上是两个)、一个“输入”类和“输出”类,以及一个继承自“输入”和“输出”类的“输入/输出”类。
但是,您从未真正直接引用基类 std::basic_ios
或 std::ios_base
,而只对任何输出流使用对 std::ostream
的引用,对任何输入流使用 std::istream
的引用(对任何输入和输出流使用 std::iostream
)。
例如,要重载输入运算符
std::istream& operator>>(std::istream& input_stream, some_type& dest);
即使对于更通用的函数,您也可以引用
标准::istream
,标准::ostream
或标准::iostream
对象。您永远不会仅仅因为遇到问题而使用基类 std::basic_ios
。
为了与您的问题以及如何解决它有更多的联系,请使用两个函数重载,一个用于输入设备,一个用于输出设备。这更有意义,因为首先您不会在检查类型和转换方面遇到问题,而且还因为这两个函数的操作会完全不同,具体取决于您是在进行输入还是输出,并且试图将其混合到一个函数中只会使代码更难维护。
所以你应该改为,例如。
void do_something(inputDevice& device);
和
void do_something(outputDevice& device);
对于下面的代码,当基类指针被分配给派生类时,我有关于多态性的问题。 当派生类的对象直接使用print函数时,输出是显而易见的。 当我使用基类指针并指向派生类的对象时,会使用基类的print函数,但输出的是派生对象的信息。有人能详细解释一下吗?谢谢!
公有派生类的对象可作为其相应基类的对象处理,这使得一些有意义的操作成为可能。例如,从某个特定基类派生出来的各种类,尽管这些类的对象彼此之间互不相同,但是仍然能够建立这些对象的链表,只要把这些对象作为基类对象处理就可以了。然而反过来是不行的,基类的对象不能自动成为派生类的对象。 常见编程错误 9.1 将基类对象作为派生类对象处理。 程序员可以用显式类型转换把基类指针强制转换为派生类指针。但是,如果要
指针变量数据类型的强制转换 必须显式强制类型转换,不允许隐式类型转换 指向空间的强制类型转换,本质上就是普通变量的强制类型转换 int a = 10; float b = 3.14; int *pa = &a; float *pb = &b; *pa = (int)*pb; // 等价于 a = (int)b; 指针本身强制类型转换,改变的是对其指向空间的引用方式(空间大小和存储结构) int
本文向大家介绍C#中的自动类型转换和强制类型转换,包括了C#中的自动类型转换和强制类型转换的使用技巧和注意事项,需要的朋友参考一下 前面已经认识了不同的数据类型,你们有没有尝试过让不同的数据类型进行运算呢? 运行结果是:1 我们把一个整型的变量赋值给了一个浮点型的变量,可以正常的输出,如果我们把一个浮点型的变量赋值给一个整型的变量呢? 这样就会报错。 为什么呢?因为我们之前说过,变量就像一个容器,
我要做的是将值存储在元素strucz数组中(该数组有一个成员void*data) 我已经把数组拼成了3个元素 我知道如果数据是int,你可以这样做,但是如果数据是空的,你怎么做呢* 我试过打字 但我得到消息预期表达式之前'in' 我的第二个问题是如何在元素结构中存储字符串(我不能使另一个成员成为字符串,它必须使用void指针) 我可以用strcopy或sscanf吗? 下面的主要样本,以防我做错了
8. 函数类型和函数指针类型 在C语言中,函数也是一种类型,可以定义指向函数的指针。我们知道,指针变量的内存单元存放一个地址值,而函数指针存放的就是函数的入口地址(位于.text段)。下面看一个简单的例子: 例 23.3. 函数指针 #include <stdio.h> void say_hello(const char *str) { printf("Hello %s\n", str); }