unique_ptr为具体类型

问题描述 投票:0回答:2
#include <iostream>
#include <memory>

class Base
{
public:
    virtual void foo() = 0;
};

class Derived : public Base
{
public:
    void foo() override { std::cout << "Derived" << std::endl; }
};

class Concrete
{
public:
    void Bar() { std::cout << "concrete" << std::endl; }
};

int main()
{
    std::unique_ptr<Concrete> ConcretePtr = nullptr;
    ConcretePtr->Bar();
    std::unique_ptr<Base> BasePtr;
    BasePtr->foo();
    return 0;
}

我假设将一个unique_ptr声明为具体类型Concrete,为Concrete类型的对象分配内存,而unique_ptr开始指向它。我的假设/理解是否正确?我问,因为ConcretePtr->Bar();打印出“混凝土”到控制台。但是,如果我创建一个指向接口Base的唯一指针,它不知道我需要的确切对象类型,也不知道在内存中分配/获取资源。

BasePtr->foo();BasePtr._Mypair._Myval2 was nullptr.失败了

为什么第一个声明std::unique_ptr<Concrete> ConcretePtr = nullptr;自己分配一个对象?如果我不希望它指向那一行代码中的某个真实对象,但只想要一个智能指针呢?

现在,如果我将声明更改为std::unique_ptr<Concrete> ConcretePtr;并将Concrete类型更改为以下内容,

class Concrete
{
    int ConcreteNum;
public:
    void Bar() 
    { 
        std::cout << "concrete" << std::endl; 
        ConcreteNum = 38;
        std::cout << ConcreteNum << std::endl;
    }
};

它失败了在ConcreteNum = 38;抱怨thisnullptr;如果这个thisnullptr然后为什么以及如何在Concrete工作的早期电话(ConcreteNum没有任何州Bar)?

此外,为什么它不会在ConcretePtr->Bar();失败(这个->需要一个具体的对象,不是吗?this在这里是什么?)但在Bar内,在那个任务中?

我也看到了与std::shared_ptr相同的问题。我不太确定声明,初始化和赋值之间的区别。请帮我理解。

我正在使用MSVC。

c++ pointers undefined-behavior unique-ptr object-lifetime
2个回答
3
投票
    std::unique_ptr<Concrete> ConcretePtr = nullptr;

我假设将一个unique_ptr声明为具体类型Concrete,为Concrete类型的对象分配内存,unique_ptr开始指向它。我的假设/理解是否正确?

好吧,你可以轻松检查。为Concrete写一个默认构造函数,打印出一些东西,这样你就可以知道实例的创建时间。运行尽可能小的程序(只需在main上面的行)。你看到了预期的产量吗?

在询问问题之前(可能在阅读documentation之后)你应该检查这些东西,但为了节省你的时间:不,该行不构造Concrete类型的对象。

您还可以明确检查unique_ptr是否正在管理对象

if (!ConcretePtr) {
  std::cout << "ConcretePtr doesn't point to anything\n";
} else {
  std::cout << "ConcretePtr owns an object\n";
}

这项检查也很简单,您可以在提出问题之前轻松完成。

我问,因为ConcretePtr->Bar();打印出“混凝土”到控制台

这是一个糟糕的测试,因为如果指针是nullptr,那么它是未定义的行为。如果你关心指针是否是nullptr,你应该在解除引用之前明确检查它,如上所述。

为了说明为什么这个测试让你感到困惑(你应该优先使用上面的那个),考虑非虚拟成员函数的可能实现(回想一下他们得到一个隐含的this指针):

// void Concrete::Bar() implemented as
void Concrete_Bar(Concrete *this)

// and ConcretePtr->Bar() implemented as
Concrete_Bar(ConcretePtr.get());

所以,你刚刚将nullptr传递给一个忽略其唯一参数的函数,而你从未测试过你认为你做过的事情。


5
投票

unique_ptr模拟指针。也就是说,它是指向另一个对象的对象。

使用nullptr初始化unique_ptr会在未指向或拥有另一个对象的状态下创建它。

这就像说Concrete* p = nullptr

以下列方式之一初始化它:

std::unique_ptr<Concrete> p{new Concrete()};

要么

std::unique_ptr<Concrete> p;  // = nullptr is implied.
p.reset(new Concrete());

或更好:

std::unique_ptr<Concrete> p = std::make_unique<Concrete>();

或者干脆:

auto p = std::make_unique<Concrete>();

但是在这种情况下要小心,如果你真的想要指向Base接口:

std::unique_ptr<Base> p = std::make_unique<Derived>();

要么

std::unique_ptr<Base> p = nullptr;

p = std::make_unique<Derived>();  // assignment from rvalue ref of compatible unique_ptr.
© www.soinside.com 2019 - 2024. All rights reserved.