unique_ptr 到具体类型

unique_ptr to a concrete type

#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 开始指向它。我的 assumption/understanding 正确吗?我问是因为 ConcretePtr->Bar(); 将 "concrete" 打印到控制台。但是,如果我创建一个指向接口 Base 的唯一指针,它不知道我需要的对象的确切类型,也不知道内存中的 allocate/acquire 资源。

这在 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。

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.
    std::unique_ptr<Concrete> ConcretePtr = nullptr;

I assume declaring a unique_ptr to a concrete type Concrete, allocates memory for an object of type Concrete and the unique_ptr starts pointing to it. Is my assumption/understanding correct ?

好吧,你可以简单地检查一下。为 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";
}

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

I ask because ConcretePtr->Bar(); prints "concrete" to the console

这是一个糟糕的测试,因为如果指针 一个 nullptr,这是未定义的行为。如果您关心指针是否是 nullptr,您应该在 取消引用之前显式检查 ,如上所述。

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

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

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

所以,您只是将 nullptr 传递给忽略其唯一参数的函数,而您从未测试过您认为已经完成的事情。