我可以使用STL容器管理不完整的类对象吗?
Can I manage incomplete class objects using STL containers?
在c++ 11中,声明一个仍然是不完整类型的类的向量是绝对无效的,对吧?我认为我只能使用不完整类型作为指针、引用、返回类型或参数类型。在(1)中搜索"不完整类型的向量",对我来说,不完整类型的容器应该是一个错误(我使用的是g++版本4.8.1)。然而,下面的代码在我的系统上编译得很好:
#ifndef SCREEN
#define SCREEN
#include <string>
using namespace std;
class Screen;
class Window_mgr{
public:
typedef vector<Screen>::size_type screenindex;
void clear(screenindex);
private:
vector<Screen> screens;
};
class Screen{
friend void Window_mgr::clear(screenindex);
public:
typedef string::size_type pos;
Screen() = default;
Screen(pos ht, pos wt): height(ht), width(wt), contents(ht*wt, ' ') { }
Screen(pos ht, pos wt, char c): height(ht), width(wt), contents(ht*wt, c) { }
private:
pos height = 0, width = 0;
pos cursor = 0;
string contents;
};
inline void Window_mgr::clear(screenindex i){
Screen& s = screens[i];
s.contents = string(s.height*s.width, ' ');
}
#endif // SCREEN
尽管Window_mgr声明了一个screen的向量,它仍然是一个不完整的类型。我的例子中的这些类实际上是基于c++入门第7.3章的。一个问题要求我定义我自己版本的Screen和Window_mgr,其中成员函数clear是Window_mgr的成员,并且是Screen的友元。除了Window_mgr也意味着包含一个屏幕向量。
如果创建一个不完整类型的向量是无效的,我该如何使用前向声明来做到这一点?如果我在Window_mgr中有一个屏幕向量,那么它的类定义必须在Screen类已经定义之后。除了Screen必须有Window_mgr的clear成员函数的友元声明,但下面的重新排列是错误的,因为Screen在不完整类型上使用了作用域操作符;
class Window_mgr;
class Screen{
friend void Window_mgr::clear(screenindex);
public:
typedef string::size_type pos;
Screen() = default;
Screen(pos ht, pos wt): height(ht), width(wt), contents(ht*wt, ' ') { }
Screen(pos ht, pos wt, char c): height(ht), width(wt), contents(ht*wt, c) { }
private:
pos height = 0, width = 0;
pos cursor = 0;
string contents;
};
class Window_mgr{
public:
typedef vector<Screen>::size_type screenindex;
void clear(screenindex);
private:
vector<Screen> screens;
};
inline void Window_mgr::clear(screenindex i){
Screen& s = screens[i];
s.contents = string(s.height*s.width, ' ');
}
我能想到的唯一方法是将成员函数友元声明替换为类的纯友元声明
class Screen{
friend class Window_mgr;
// other stuff
}
但这不是问题想要我做的。
标准容器目前不支持不完整类型;标准容器模板可以用不完整类型实例化吗?也就是说,实现可以选择支持不完整类型作为扩展,但这会使你的代码不可移植。
论文n4371对标准容器的最小不完整类型支持,修订版2已被纳入最新的c++标准草案(n4527),因此除非出现意外情况,否则很可能在c++ 17中支持vector
, list
和forward_list
的不完整类型。
有一种方法可以满足"c++入门"中的要求,而不依赖于实现扩展或c++ 17:
-
clear
是Window_mgr
的成员函数; -
Window_mgr::clear()
是Screen
的朋友; -
Window_mgr
is包含Screen
s的矢量:
你可以让Screen
成为Window_mgr
的嵌套类:
class Window_mgr{
public:
typedef std::size_t screenindex;
void clear(screenindex);
class Screen{
friend void Window_mgr::clear(screenindex);
public:
// ...
};
// ...
private:
vector<Screen> screens;
};
即使在这里,我也不得不调整类型screenindex
的定义来打破依赖循环。
简短的回答是,在不完整类型中使用vector是非法的。就像使用任何其他STL容器一样。某些东西可以编译,甚至可以完美地工作,并不意味着它是合法的。
你在(1)中提到的文章引用了这篇文章。这给了一个非常清楚(尽管很长)的解释。根据我的解释,它归结为以下内容:
- 通常在不完整类型上使用STL模板是非法的
- 在实践中,它与一些STL容器和库一起工作
- Vector是一种你可以放心使用它的情况。我提到的文章的注释章节的最后一点是:
但是也许它应该在具体情况的基础上放松,vector看起来像是这种特殊情况处理的良好候选:它是一个标准容器类,有很好的理由用不完整类型实例化它,并且标准库实现者希望使它工作。
你描述的另一个问题很常见。为了简化它,你可以这样说。假设你有两个类:
class Foo
{
std::vector<Bar> myVector;
}
class Bar
{
std::vector<Foo> myVector;
}
这似乎是一个合理的要求。但在标准的限制下,这是不可能的。根据标准解决这个问题的一种方法是使用指针列表(std::shared_pointer
在这里很有用)。或者,放弃不遵循标准而使用前向声明。
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 将 std::variant 与 gmock 1.8 对象一起使用时编译错误
- 是否可以使C++类成为Objc类的委托
- 是否可以使一个类成为两个不同层次结构的子类?
- 复制一个对象并使两者共享一个成员变量 (C++)
- 如何包装对象,使它们成为无法交互的单独类型?
- 是否有更好的方法来封装成员对象可以访问的共享存储池?
- 为什么对象可以"moved"甚至缺少移动构造函数和移动赋值运算符?
- 是否有编译器标志可以使较新的 gcc 版本像旧版本一样构建
- 提升序列化仅适用于主要?当我在其他对象中使用时,继续说"has no member named ‘serialize’"
- C 可以使destuructor不称为班级成员和基类攻击方的灾难
- 为什么临时对象可以绑定到常量引用?
- 可以使未命名的结构静态
- 是否可以使整数仅收到一个单个数字而不是两个接收输入
- 为什么通过引用传递从 std::function 派生的对象会使程序崩溃?
- 如何使对象可以访问上课的其余部分
- 是否可以更早地销毁对象,使其存储内存被后续对象重用
- 是否可以使指向派生类对象的基类指针访问在派生类中声明的新成员
- 可以使类名和对象名相同服务于singleton的机制
- 在 c++ 中是否有任何方法可以使一个类的函数,每个类的对象都为该函数做不同的事情?