neu*_*rte 2 c++ templates pure-virtual
在我当前正在编写的应用程序中,我创建了一个具有纯虚函数的模板类,然后又一个类继承了前者的实例并实现了虚函数。虚函数是从父级的构造函数调用的,子级也使用此函数。由于链接器错误,我无法构建此代码,我也无法弄清原因。这是代码的简化版本,可以重现我遇到的问题。
template <typename T> class Foo
{
public:
Foo(T a)
{
x = a;
echo();
}
protected:
T x;
virtual void echo() = 0;
};
class Bar : public Foo<int>
{
public:
Bar(int a) : Foo<int>(a)
{
}
void echo();
};
void Bar::echo()
{
cout << "value: " << x << endl;
}
int main(int argc, char* argv[])
{
Bar bar(100);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
链接器错误在MSVC中显示如下:
purevirttest.obj:错误LNK2019:未解析的外部符号“受保护:虚拟void __thiscall Foo :: echo(void)”(?echo @?$ Foo @ H @@ MAEXXZ)在函数“ public:__thiscall Foo :: Foo(int)中引用” )“(?? 0?$ Foo @ H @@ QAE @ H @ Z)
如果我将调用从Foo的构造函数中移到echo(),那么代码会很好地构建和执行,我可以毫无问题地调用bar.echo()。问题是我真的很想要构造函数中的那个函数。对此谜的任何解释都表示赞赏。
James McNellis的回答“您不能echo()从的构造函数调用Foo<T>” 几乎是正确的。
您不能从Foo<T>构造函数虚拟地调用它,因为在构造函数的主体Foo<T>执行对象时,它的类型是Foo<T>。尚无派生类。然后echo()像在您的代码中一样虚拟调用,然后转到纯虚拟函数:砰,死了。
但是,您可以提供一个纯虚函数(如)的实现echo(),然后Foo::echo()从Foo构造函数中非虚调用(如)。:-)除非调用Foo实现。看来您想调用派生类的实现。
现在关于您的问题:
“我真的很想要构造函数中的那个函数。”
好吧,在我编写此代码时,您的(无效)代码如下所示:
template <typename T> class Foo
{
public:
Foo(T a)
{
x = a;
echo();
}
protected:
T x;
virtual void echo() = 0;
};
class Bar : public Foo<int>
{
public:
Bar(int a) : Foo<int>(a)
{
}
void echo();
};
void Bar::echo()
{
cout << "value: " << x << endl;
}
int main(int argc, char* argv[])
{
Bar bar(100);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
据我了解您的问题描述,您希望Foo构造函数调用echo继承自的任何类的实现Foo。
有很多方法可以做到这一点。它们都是关于将派生类的实现知识带到基类的。
一种称为CRTP,即好奇重复性模板模式,它可以针对您的特定问题进行如下调整:
#include <iostream>
template< class XType, class Derived >
class Foo
{
public:
Foo( XType const& a )
: state_( a )
{
Derived::echo( state_ );
}
protected:
struct State
{
XType x_;
State( XType const& x ): x_( x ) {}
};
private:
State state_;
};
class Bar
: public Foo< int, Bar >
{
private:
typedef Foo< int, Bar > Base;
public:
Bar( int a ): Base( a ) {}
static void echo( Base::State const& );
};
void Bar::echo( Base::State const& fooState )
{
using namespace std;
cout << "value: " << fooState.x_ << endl;
}
int main()
{
Bar bar(100);
}
Run Code Online (Sandbox Code Playgroud)
以上是一个不错的解决方案,但也不是很好。如果您的实际问题是从基类构造函数中调用派生类的非静态成员函数,则唯一的“好”答案是Java或C#,这使您可以执行此操作。有意在C ++中不支持它,因为很容易在无意间尝试访问派生类对象中尚未初始化的内容。
无论如何,几乎总是有编译时解决方案的地方,还有运行时解决方案的地方。
您可以简单地传递要作为构造函数参数执行的函数,如下所示:
#include <iostream>
template< class XType >
class Foo
{
protected:
struct State
{
XType x_;
State( XType const& x ): x_( x ) {}
};
public:
Foo( XType const& a, void (*echo)( State const& ) )
: state_( a )
{
echo( state_ );
}
private:
State state_;
};
class Bar
: public Foo< int >
{
private:
typedef Foo< int > Base;
public:
Bar( int a ): Base( a, echo ) {}
static void echo( Base::State const& );
};
void Bar::echo( Base::State const& fooState )
{
using namespace std;
cout << "value: " << fooState.x_ << endl;
}
int main()
{
Bar bar(100);
}
Run Code Online (Sandbox Code Playgroud)
如果研究这两个程序,您可能会注意到一个细微的差异(除了编译时间与运行时知识转移之外)。
最后,有涉及脏转换的解决方案,并且C ++类型系统中还有一个漏洞,可让您使用成员指针访问受保护的基类状态而不进行转换。前者是危险的,而后者是模糊的并且可能是低效的。所以,不要。
但希望上述解决方案之一适合您,或进行一些适当的调整。
哦,顺便说一下,您似乎是其中的一个更一般的问题集被称为DBDI,即初始化期间的动态绑定。您可以在C ++常见问题解答项目23.6中找到对它的更一般的处理,但是,有没有一种方法可以模拟该行为,就像动态绑定对基类的构造函数中的this对象起作用一样?。另外,对于DBDI的特殊情况,您希望基类构造的一部分由派生类控制/提供,请参阅我的博客条目“如何通过使用Parts Factories避免后期构造”。
干杯,……
| 归档时间: |
|
| 查看次数: |
2079 次 |
| 最近记录: |