12

很长一段时间以来,我已经意识到指针,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
}

但是智能指针只是指针的包装器。

我想知道是否有办法避免这种情况并在不使用指针的情况下利用多态类。

4

6 回答 6

7

你不能避免这个指针。如果您不喜欢它们,C++ 将不会成为您的语言,因为如果您想做任何多态的事情,您将不得不使用指针来克服最琐碎的用途。在堆上构造对象,即使用new是避免堆栈构造对象的作用域生命周期的方法,如果你想在条件分支中创建东西然后将它们分配给父作用域中的变量,你必须这样做 - 如果你不这样做'不需要这样做,您实际上也不需要多态性,因为您的类型在编译时都是可确定的。没有办法解决这个问题。

当然,使用智能指针,它们确实可以帮助避免指针生命周期的问题,但是无论您最终使用什么非常酷的抽象,都会在某个地方存在指针。

于 2012-08-31T10:38:23.177 回答
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);
}
于 2012-08-31T10:27:34.357 回答
1

我不知道你为什么要不惜一切代价避免任何形式的指针。std::unique_ptr使用/绝对没std::shared_ptr问题(当然,当有需要时)。智能指针不仅仅是指针周围的“包装器”。它们让您可以在不同的语义之间进行选择,并具有不同的用途。

于 2012-08-31T10:25:31.420 回答
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某个地方进行定义,不幸的是,当前标准库中缺少该定义,但最终会被修改。)

于 2012-08-31T10:35:01.830 回答
1

我想知道是否有办法避免这种情况并在不使用指针的情况下利用多态类。

不,这就是 C++ 最初的设计方式。为避免切片,您需要使用指针或引用。

于 2012-08-31T10:35:18.133 回答
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

此解决方案有几个限制,很少可以使用。请注意,对象是在第一次函数调用时静态创建的,并将持续到程序结束。

于 2012-08-31T10:38:46.700 回答