很长一段时间以来,我一直认为指针,new
和delete
在C ++中有点不必要,除非处理长寿命对象,引用是一个更适合RAII模型的更清晰的替代方案。但是,在C ++中使用动态多态时,我仍然无法确定如何避免指针。
我们有这些关系:
class A
{
public:
virtual void a() const = 0;
};
class B : public A
{
virtual void a() const
{
std::cout << "B";
}
};
class C : public A
{
virtual void a() const
{
std::cout << "C";
}
};
void invoke(const A& obj)
{
obj.a();
}
int main()
{
B b;
invoke(b); // Prints B
}
对象可以作为引用传递给invoke
,并且没有涉及指针(好吧,至少从程序员的角度来看)。但是,上面的例子基本上是静态多态。
如果我想让b
的类型依赖于其他东西,我将不得不使用指针:
int main()
{
A* a;
if (something)
a = new B;
else
a = new C;
invoke(*a); // Prints B
delete a;
}
这对我来说看起来很难看。当然,我可以使用智能指针:
int main()
{
std::unique_ptr<A> a;
if (something)
a.reset(new B);
else
a.reset(new C);
invoke(*a); // Prints B
}
但智能指针只是指针的包装器。
我想知道是否有办法避免这种情况并在不使用指针的情况下使用多态类。
答案 0 :(得分:6)
你无法避免指出这一点。如果你不喜欢它们,C ++不会成为你的语言,因为如果你想做任何多态的事情,你将不得不使用指针来克服最微不足道的用途。在堆上构造对象,即使用new
是如何避免堆栈构造对象的作用域生存期,如果要在条件分支内部进行操作然后将它们分配给父作用域中的变量,则必须执行此操作 - 如果您不需要这样做,您实际上也不需要多态性,因为您的类型在编译时都可以确定。没有办法解决这个问题。
当然,使用智能指针,它们确实可以帮助避免指针生命周期的问题,但无论你最终使用什么样的酷抽象,都会在某处出现指针。
答案 1 :(得分:4)
鉴于你的上一个例子,你可以避免使用指针(虽然我没有看到智能指针的大问题)
struct AHolder {
std::shared_ptr<A> a;
operator A const&() const {
return *a;
}
AHolder(bool something)
: a(something?std::make_shared<B>():std::make_shared<C>()) {
}
};
这使您可以使用持有者 - 如果它是实际对象:
int main() {
AHolder a(true); // or whatever argument you like
invoke(a);
}
答案 2 :(得分:1)
我不知道为什么你要不惜一切代价避免任何形式的指针。使用std::unique_ptr
/ std::shared_ptr
(当然需要时)是绝对正确的。
智能指针不是指针周围的“包装”。它们允许您在不同的语义之间进行选择,并具有不同的用途。
答案 3 :(得分:1)
您可能正在考虑的用例是某种 factory 的用例,您可以使用智能指针完美地提供服务,而且根本没有new
:
std::unique_ptr<Message> parse_message(char const * buf, std::size_t len)
{
if (buf[0] == 'A') { return make_unique<RequestMsg>(buf + 1, len - 1); }
if (buf[0] == 'R') { return make_unique<AnswerMsg>(buf + 1, len - 1); }
return nullptr;
}
用法:
auto msgptr = parse_msg(buf, len);
(你需要在某个地方定义make_unique
,遗憾的是当前标准库中没有这个定义但最终会被修改。)
答案 4 :(得分:1)
我想知道是否有办法避免这种情况并在不使用指针的情况下使用多态类。
不,这就是c ++首先设计的方式。为避免切片,您需要使用指针或引用。
答案 5 :(得分:0)
如果您需要动态创建对象并将其从功能范围中移除,则无法实现。你可以做到最好 - 在工厂和智能指针中隐藏对象创建。
你也可以考虑函数范围内的静态对象,但这不是一个好的解决方案 - 你可以用这种方式创建有限数量的对象:
template <class T, int Instance> T& create()
{
static T instance;
return instance;
}
MyClass& obj1 = create<MyClass, 1>();
MyClass& obj2 = create<MyClass, 2>();
MyClass& obj3 = create<MyClass, 3>();
// etc
此解决方案有一些限制,很少可以使用。请注意,在第一次函数调用时会创建静态对象,并且该对象将一直持续到程序结束。