我正在尝试为类编写的方法遇到一些麻烦。我有班级符号和班级终端。 class终端扩展了类符号,但类符号的方法之一需要返回一个向量。 E.g:
#ifndef SYMBOL_H
#define SYMBOL_H
#include "terminal.h"
#include <vector>
using namespace std;
class symbol {
public:
vector<terminal> first();
virtual void polymorphable();
};
#endif
定义了班级终端:
#ifndef TERMINAL_H
#define TERMINAL_H
#include "symbol.h"
using namespace std;
class terminal: public symbol {
// ...
};
#endif
但是,在执行此操作时,构建时出现两个错误,其中一个或另一个首先出现:“'terminal':未声明的标识符”在定义向量函数的行上,“'symbol':基类未定义“与终端类定义一致。
如何解决这个'a要求b','b需要'问题?
答案 0 :(得分:3)
class terminal;
class symbol
{
std::vector<terminal> first();
// ...
};
根据C ++标准,有人猜测这种方法是未定义的 @Ben Voight指出:
C ++ 03标准第17.6.4.8节说:
“特别是,在以下情况下效果未定义:...如果在实例化模板组件时将不完整类型用作模板参数,除非特别允许该组件。
std::vector<X> f();
是std::vector<X>
实例化,正在讨论 here 。如果那里的答案证明那么这个答案没有用,我会删除相同的,否则这一点仍然有效。
答案 1 :(得分:2)
编辑:标准可能不允许以下内容(请参阅注释)。在这种情况下,您根本无法获得正确的循环依赖关系:如果A
的大小取决于B
类型成员的大小,但B
的大小取决于大小对于A
类型的成员,那么这样的定义根本没有意义。
我不完全确定这是否适用于您的情况,因为您只声明了一个返回类型不完整的函数,允许 。见服务员question by James;希望我们能在那里得到明确答案。
只需向前声明terminal
:
class terminal;
class symbol
{
std::vector<terminal> first();
// ...
};
您可以转发声明只需要不完整类型的任何内容。不完整类型可用于形成指针,引用,函数签名。只有当该类型的变量使用时,才需要知道完整类型。
答案 2 :(得分:1)
Base
课程不需要了解Derived
课程。这是面向对象设计中的一个重要原则。您可以将基类更改为:
class symbol {
public:
vector<symbol*> first();
virtual void polymorphable();
};
现在,first()
返回指向基类的指针向量。使用多态,每个指针实际上可以指向派生类。请注意,我将其更改为使用指针。如果您将其更改为只会vector<symbol>
无效。
另外,如果你真的需要Base类来了解Derived类的存在,你可以转发声明派生类:
#ifndef SYMBOL_H
#define SYMBOL_H
#include "terminal.h"
#include <vector>
using namespace std;
class terminal; // forward declare the existence of this class
class symbol {
public:
vector<terminal*> first(); // change to be a vector of pointers
// to avoid issues with incomplete type
virtual void polymorphable();
};
#endif
答案 3 :(得分:1)
使用前向声明。
詹姆斯 - 注释声明与实例不同。这段代码工作正常
#include <vector>
class terminal; <--- TELLING THE COMPILER MAY USE terminal in the future.
class symbol
{
std::vector<terminal> first(); <--- NOTE THE COMPILER DOES NOT NEED TO KNOW HOW TO CONSTRUCT EITHER
// ...
};
class terminal: public symbol < --- TELLS COMPILER THAT terminal INHERITS symbol i.e. CONTAINING THE METHOD first
{
int wibble;
};
int main()
{
symbol s;
return 0;
}
Als - 你是对的。
答案 4 :(得分:0)
我认为奇怪的重复模板模式可以让你脱离这一个:
template<typename terminal_type>
class symbol_pattern
{
public:
std::vector<terminal_type> first();
virtual void polymorphable();
};
class terminal : public symbol_pattern<terminal>
{
};
typedef symbol_pattern<terminal> symbol;
答案 5 :(得分:0)
前向声明+智能指针(虽然这会将内容存储在堆上而不是堆栈中......可能是不合需要的)
#ifndef SYMBOL_H
#define SYMBOL_H
#include <vector>
#include <memory>
using namespace std;
class terminal; // Make a forward declaration like this
class symbol {
public:
vector<shared_ptr<terminal>> first();
virtual void polymorphable();
};
#endif
答案 6 :(得分:0)
不要让单个成员函数返回容器,而是考虑使用返回迭代器范围的begin()
和end()
函数,类似于标准库容器本身的函数:
class terminal;
class terminal_iterator { /* defined appropriately */
struct symbol
{
terminal_iterator begin_terminals() const;
terminal_iterator end_terminals() const;
};
如果你已经在某个地方有std::vector<terminal>
迭代,你可以typedef terminal const* terminal_iterator;
(或使用类似的typedef)并相应地定义成员函数。
如果你没有容器(即,成员函数会实现序列本身),你可以考虑编写自己的生成序列的迭代器类。
提供begin()
和end()
范围访问器有时比简单地为容器提供访问器要多一些,但范围访问器可以提供更大的灵活性和抽象。