使用动态多态性时如何避免指针? 很久以来,我一直注意到指针,新< /COD>和删除>代码>,除非处理长寿命对象,否则C++中有点不必要,引用是更适合RAII模型的更干净的替代。但是,在C++中使用动态多态性时,仍然无法确定如何避免指针。

使用动态多态性时如何避免指针? 很久以来,我一直注意到指针,新< /COD>和删除>代码>,除非处理长寿命对象,否则C++中有点不必要,引用是更适合RAII模型的更干净的替代。但是,在C++中使用动态多态性时,仍然无法确定如何避免指针。,c++,pointers,polymorphism,C++,Pointers,Polymorphism,好的,我们有这些分类: 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";

好的,我们有这些分类:

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
}
对我来说,这看起来很难看。当然,我可以使用智能指针:

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
}
intmain()
{
std::唯一的ptr a;
如果(某物)
a、 重置(新B);
其他的
a、 重置(新C);
调用(*a);//打印B
}
但智能指针只是指针的包装


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

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

在上一个示例中,您可以避免使用指针(尽管我并不认为智能指针有什么大问题)


您可能正在考虑的用例是一种工厂,您可以使用智能指针完美地为其提供服务,而无需任何
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;
}
(您需要在某个地方定义
make_unique
,不幸的是,当前标准库中缺少该定义,但最终将对其进行修改。)

我想知道是否有一种方法可以避免这种情况,并在不使用指针的情况下利用多态类


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

对此,您不能避免使用指针。如果你不喜欢C++,C++就不可能成为你的语言,因为如果你想做任何多态的事情,你就必须用指针来完成最琐碎的用法。在堆上构造对象,即使用
new
是避免堆栈构造对象的作用域生存期的方法,如果您想在条件分支中创建一些东西,然后将它们分配给父范围中的变量,那么必须这样做——如果您不需要这样做,那么实际上也不需要多态性,因为您的类型在编译时都是可确定的。这是没办法的


使用智能指针当然,它们确实有助于避免指针生命周期的问题,但不管最终使用的是什么非常酷的抽象,都会有指针存在于其中。

如果需要动态创建对象并将其保留在函数范围之外,则没有办法。这里您可以做的最好的事情是在工厂和智能指针中隐藏对象创建

也可以在函数作用域中考虑静态对象,但这不是一个好的解决方案——这样只能创建有限数量的对象:

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
模板T&create()
{
静态T实例;
返回实例;
}
MyClass&obj1=create();
MyClass&obj2=create();
MyClass&obj3=create();
//等

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

if(cond)invoke(B(…))else invoke(C(…)
,又称函数样式?或者在C++11和lambdas中,如果公共部分很大,并且您不需要为其提供额外的全局函数。@Xeo在这个特定的示例中它可以工作,但这并不总是适用的。您所称的静态多态性根本不是静态多态性<代码>调用并不确切地知道其参数的类型。您只认为它是静态多态性,因为您可以看到传递的代码和类型@Tibor:如果您需要有条件地存储一个对象或另一个对象,则这不起作用,true。在这些情况下,您将无法回避使用指针,因为它们对于动态多态性至关重要。@Hbcdev:我认为OP的意思是排除所有动态绑定可以被元编程取代的情况,因为所有基于运行时类型的决策都可以在编译时知道。第二,对象的实际类型取决于运行时信息,您失去了一些优化选项。所以——如果某个东西是一个
constexpr
,你可以用一种说法称之为“静态多态性”。当然,这是可行的。然而,我的问题是,更多的是关于完全避免使用
new
*
运算符的问题。@Tibor:嗯,
*
类型限定符以及
*
运算符隐藏在这个工具中(可以很容易地制作成通用模板),正如智能指针隐藏了它们对原始指针的使用一样,
make_unique
的定义包含了对
new
的调用。从根本上说,解决这个问题离不开指针,尽管你可以做出各种漂亮而有用的抽象。+1让我意识到我忘记了
make
例程(并且使用了脏的ol'
new
)。@Matthewn:当然可以。标准库中的每个动态容器都调用
::new
left-right和center。但是这里的要点是建立一个通用的<代码>新的<代码> -C++的无指针的成语!请记住包装函数在异常安全性方面的区别。单例通常是丑陋的,在这种情况下,特别危险。这实际上是一个用于大多数单例对象的生成器。@当然是位掩码,这是因为我写的这是可能的,但不是好的解决方案。请注意,实际上您可以创建许多相同类型的对象,所以这与单例无关……这就是为什么我没有投票反对。同意我先前评论的人也不同意
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);
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