子类定义新成员变量时能避免dynamic_cast吗?
Can I avoid dynamic_cast when subclasses define new member variables?
我正在尝试以 "modern" C++ 风格编写应用程序,同时尝试学习如何这样做,并且陷入了一个应该相当基本的设计问题。
该应用程序会索引各种内容,例如 files/folders、音乐(通过某些音乐播放器 API,而不是文件系统),也许在未来的书签中等等。
问题在于代表这些东西。我的计划是使用基数 class,例如IndexedObject
,然后我将 class 子 IndexedFile
、IndexedSong
等等。
这些 objects 需要有一些共同的成员,有些则没有。所有种类都需要一个图标和一个名称才能在应用程序中显示,所以这些显然放在 IndexedObject
中。然而,索引文件需要完整路径,而索引歌曲需要艺术家、专辑和标题,但我们可能不知道它的路径(它甚至可能不在磁盘上)。将所有这些存储在一个 class 中似乎非常丑陋,在那种情况下,我仍然需要一个 "type" 成员来弄清楚一个特定的 class 实例应该代表什么。
对于程序中我有一个 IndexedObject
但只需要访问特定子 class(例如 IndexedFile
知道的信息)的部分,是否有更好的选择而不是使用 dynamic_cast
,或者这正是应该使用的时间?
"better",我指的是更好的任何合理定义,例如具有更高的性能、更安全等等。顺便说一句,其他选项可能包括完全不同的设计。这个基础 class/subclass 设计是我第一个想到的。
更新:
一些评论者要求提供代码。但是,我真的没有太多与这个特定问题相关的代码;问题是我应该如何设计和编码(subclass 方法是否正确)。我可以 super-specific 满足我的需要,但对我以外的任何人来说,答案都会变成 near-useless。
不过,我可以提供一些关于目标的额外细节,所以如果您需要,请继续阅读。
该程序对背景中提到的各种事物进行索引,并将它们存储在某种容器类型(在我的例子中为 QVector)以便于检索。用户通过一些热键启动应用程序,然后输入搜索词。对于每个键入的字母,该应用程序会过滤索引并显示匹配项。
对于找到的每个匹配项,UI 需要知道其类型。如果是一首歌,也许我们想将其格式化为“艺术家 - 曲目名称(来自专辑 )“ 或者其他的东西;如果是书签,可能会显示域名和部分URL,等等。
所以换句话说,如果我使用subclass方法,我可能会这样做:
// The real code would of course have a bit more meat with some basic methods
class IndexedObject {
string name;
image icon;
};
class IndexedSong : public IndexedObject {
string artist;
string title;
};
class IndexedBookmark : public IndexedObject {
some_url_type url;
};
void displayObject(const IndexedObject &obj) {
// Pseudo-C++ follows
if (obj is IndexedSong) {
display icon, artist and title
}
else if (obj is IndexedBookmark) {
display icon, name and URL;
}
}
...这个例子的问题在于问题不是如何使用 dynamic_cast,上面的例子似乎暗示我必须这样做,而是是否有一个 完全解决这个问题的不同方法 完全避免了dynamic_cast。
而不是使用dynamic_cast,首选的方法是将必须为不同的类做不同的部分放入虚函数中。要查找和了解的术语是 "polymorphism"。
P.S。也许使用继承根本不是一个好主意——可能有些歌曲也是文件。组合可能会更好。
多态数据存储类型对于 C++ 来说有点问题,因为良好的 OOP 风格规定多态行为应该放在子类本身中……也就是说,IndexedObject
会有一个纯虚拟 play()
函数,IndexedSong
的实现将播放歌曲,IndexedVideo
的实现将播放视频,等等。但如果它是其他东西在消耗数据,那么就很难为它创建一个漂亮的抽象接口。
dynamic_cast
、指定类型的枚举等几乎都是在这种情况下使用的。
对于您的代码,您可以在基 class 中定义一个虚函数,并让子 class 实现该函数,这样可以避免 dynamic_cast。代码将是这样的:
class IndexedObject {
string name;
image icon;
public:
virtual void display() = 0;
};
class IndexedSong : public IndexedObject {
string artist;
string title;
public:
virtual void display()
{/////}
};
class IndexedBookmark : public IndexedObject {
some_url_type url;
public:
virtual void display()
{/////}
};
void displayObject(const IndexedObject &obj) {
obj.display()
}
Open-Closed principle就是为了处理这种情况,你可以在url中找到更多的细节,它讲到如何不使用dynamic_cast。
我正在尝试以 "modern" C++ 风格编写应用程序,同时尝试学习如何这样做,并且陷入了一个应该相当基本的设计问题。
该应用程序会索引各种内容,例如 files/folders、音乐(通过某些音乐播放器 API,而不是文件系统),也许在未来的书签中等等。
问题在于代表这些东西。我的计划是使用基数 class,例如IndexedObject
,然后我将 class 子 IndexedFile
、IndexedSong
等等。
这些 objects 需要有一些共同的成员,有些则没有。所有种类都需要一个图标和一个名称才能在应用程序中显示,所以这些显然放在 IndexedObject
中。然而,索引文件需要完整路径,而索引歌曲需要艺术家、专辑和标题,但我们可能不知道它的路径(它甚至可能不在磁盘上)。将所有这些存储在一个 class 中似乎非常丑陋,在那种情况下,我仍然需要一个 "type" 成员来弄清楚一个特定的 class 实例应该代表什么。
对于程序中我有一个 IndexedObject
但只需要访问特定子 class(例如 IndexedFile
知道的信息)的部分,是否有更好的选择而不是使用 dynamic_cast
,或者这正是应该使用的时间?
"better",我指的是更好的任何合理定义,例如具有更高的性能、更安全等等。顺便说一句,其他选项可能包括完全不同的设计。这个基础 class/subclass 设计是我第一个想到的。
更新:
一些评论者要求提供代码。但是,我真的没有太多与这个特定问题相关的代码;问题是我应该如何设计和编码(subclass 方法是否正确)。我可以 super-specific 满足我的需要,但对我以外的任何人来说,答案都会变成 near-useless。
不过,我可以提供一些关于目标的额外细节,所以如果您需要,请继续阅读。
该程序对背景中提到的各种事物进行索引,并将它们存储在某种容器类型(在我的例子中为 QVector)以便于检索。用户通过一些热键启动应用程序,然后输入搜索词。对于每个键入的字母,该应用程序会过滤索引并显示匹配项。
对于找到的每个匹配项,UI 需要知道其类型。如果是一首歌,也许我们想将其格式化为“艺术家 - 曲目名称(来自专辑 )“ 或者其他的东西;如果是书签,可能会显示域名和部分URL,等等。
所以换句话说,如果我使用subclass方法,我可能会这样做:
// The real code would of course have a bit more meat with some basic methods
class IndexedObject {
string name;
image icon;
};
class IndexedSong : public IndexedObject {
string artist;
string title;
};
class IndexedBookmark : public IndexedObject {
some_url_type url;
};
void displayObject(const IndexedObject &obj) {
// Pseudo-C++ follows
if (obj is IndexedSong) {
display icon, artist and title
}
else if (obj is IndexedBookmark) {
display icon, name and URL;
}
}
...这个例子的问题在于问题不是如何使用 dynamic_cast,上面的例子似乎暗示我必须这样做,而是是否有一个 完全解决这个问题的不同方法 完全避免了dynamic_cast。
而不是使用dynamic_cast,首选的方法是将必须为不同的类做不同的部分放入虚函数中。要查找和了解的术语是 "polymorphism"。
P.S。也许使用继承根本不是一个好主意——可能有些歌曲也是文件。组合可能会更好。
多态数据存储类型对于 C++ 来说有点问题,因为良好的 OOP 风格规定多态行为应该放在子类本身中……也就是说,IndexedObject
会有一个纯虚拟 play()
函数,IndexedSong
的实现将播放歌曲,IndexedVideo
的实现将播放视频,等等。但如果它是其他东西在消耗数据,那么就很难为它创建一个漂亮的抽象接口。
dynamic_cast
、指定类型的枚举等几乎都是在这种情况下使用的。
对于您的代码,您可以在基 class 中定义一个虚函数,并让子 class 实现该函数,这样可以避免 dynamic_cast。代码将是这样的:
class IndexedObject {
string name;
image icon;
public:
virtual void display() = 0;
};
class IndexedSong : public IndexedObject {
string artist;
string title;
public:
virtual void display()
{/////}
};
class IndexedBookmark : public IndexedObject {
some_url_type url;
public:
virtual void display()
{/////}
};
void displayObject(const IndexedObject &obj) {
obj.display()
}
Open-Closed principle就是为了处理这种情况,你可以在url中找到更多的细节,它讲到如何不使用dynamic_cast。