c++ - 将 char* 替换为 shared_ptr<char>

标签 c++ pointers shared-ptr

我有一个结构如下:

struct P
{
    enum {INT, FLOAT, BOOLEAN, STRING, ERROR} tag;
    union
    {
        int a;
        float b;
        bool c;
        const char* d;
    };
};

我正在使用 Cereal 库来序列化它,并且 Cereal 不支持原始指针。我正在替换 const char* dconst shared_ptr<char> d 。我面临 3 个问题:

  1. 将 char* 转换为共享指针:

    char* x = //first element of char array
    d = shared_ptr<char> (x); // is this the right way?
    
  2. 处理如下任务:

    string s = "hello";
    d = s.c_str(); // how to convert the c_str() to shared_ptr<char>?
    
  3. 据我所知,shared_ptr 处理的指针似乎与原始指针非常不同。我可以安全地使用这个shared_ptr作为字符数组而不产生任何副作用吗?

最佳答案

首先要说的是,您正在使用 union 体。 C++ 中的 union 确实很难正确使用。你真的需要 union 吗?

如果您确实需要 union ,请使用boost::variant。它为您解决所有复杂问题。

接下来,我们使用 C++,而不是 C。让我们像它一样行事。去掉 const char *。这是一个地雷。这就是为什么 Cereal 不支持它。他们正在做正确的事。将其替换为原来的内容。一个std::string

编辑:

好的。你自找的。这是使用受歧视 union 的解决方案。

现在,还记得我说过在 C++ 中 union 很难吗?

过去 15(20?)年里我几乎每天都在编写 C++。我是标准进展的狂热追随者,我总是使用最新的工具,并且我要求团队中的人员彻底了解该语言和标准库......而且我还不确定这个解决方案是否完全强壮的。我需要花一天时间编写测试才能真正确定......因为受歧视的 union 真的很难得到正确对待。

编辑2:

修复了“construct from const char*”错误(告诉过你这很难......)

确定不想使用boost::variant吗?

没有?好吧:

#include <iostream>
#include <string>

struct error_type {};
static constexpr error_type as_error = error_type {};

struct P
{
    enum {
        INT, FLOAT, BOOLEAN, STRING, ERROR
    } _tag;

    union data
    {
        data() {}
        ~data() {} // define a destructor that does nothing. We need to handle destruction cleanly in P

        int a;
        double b;   // use doubles - all calculation are performed using doubles anyway
        bool c = false;  // provide a default constructor
        std::string d;  // string or error
    } _data;

    // default constructor - we must initialised the union and the tag.

    P() : _tag { BOOLEAN }, _data {} {};

    // offer constructors in terms of the various data types we're storing. We'll need to descriminate
    // between strings and errors...

    P(int a) : _tag (INT) {
        _data.a = a;
    }

    P(double b) : _tag (FLOAT) {
        _data.b = b;
    }

    P(bool c) : _tag (BOOLEAN) {
        _data.c = c;
    }

    P(std::string s) : _tag(STRING)
    {
        new (std::addressof(_data.d)) std::string(std::move(s));
    }

    // provide a const char* constructor... because const char* converts to bool
    // more readily than it does to std::string (!!!)
    P(const char* s) : P(std::string(s)) {}

    P(std::string s, error_type) : _tag(ERROR)
    {
        new (std::addressof(_data.d)) std::string(std::move(s));
    }

    // destructor - we *must* handle the case where the union contains a string
    ~P() {
        destruct();
    }

    // copy constructor - we must initialise the union correctly

    P(const P& r)
    : _tag(r._tag)
    {
        copy_construct(r._data);
    }

    // move constructor - this will be particularly useful later...

    P(P&& r) noexcept
    : _tag(r._tag)
    {
        steal_construct(std::move(r._data));
    }

    // assignment operator in terms of constructor
    P& operator=(const P& p)
    {
        // this line can throw
        P tmp(p);

        // but these lines will not
        destruct();
        steal_construct(std::move(tmp._data));
        return *this;
    }

    // move-assignment in terms of noexcept functions. Therefore noexcept
    P& operator==(P&& r) noexcept
    {
        destruct();
        _tag = r._tag;
        steal_construct(std::move(r._data));
        return *this;
    }

    // don't define swap - we have a nothrow move-assignment operator and a nothrow
    // move constructor so std::swap will be optimal.

private:

    // destruct our union, using our tag as the type switch
    void destruct() noexcept
    {
        using namespace std;
        switch (_tag) {
            case STRING:
            case ERROR:
                _data.d.~string();
            default:
                break;
        }
    }

    /// construct our union from another union based on our tag
    void steal_construct(data&& rd) noexcept
    {
        switch(_tag) {
            case INT:
                _data.a = rd.a;
                break;
            case FLOAT:
                _data.b = rd.b;
                break;
            case BOOLEAN:
                _data.c = rd.c;
                break;
            case STRING:
            case ERROR:
                new (std::addressof(_data.d)) std::string(std::move(rd.d));
                break;
        }
    }

    // copy the other union's data based on our tag. This can throw.
    void copy_construct(const data& rd)
    {
        switch(_tag) {
            case INT:
                _data.a = rd.a;
                break;
            case FLOAT:
                _data.b = rd.b;
                break;
            case BOOLEAN:
                _data.c = rd.c;
                break;
            case STRING:
            case ERROR:
                new (std::addressof(_data.d)) std::string(rd.d);
                break;
        }
    }

public:

    // finally, now all that union boilerplate malarkey is dealt with, we can add some functionality...

    std::string report() const {
        using namespace std::string_literals;
        using std::to_string;

        switch (_tag)
        {
            case INT:
                return "I am an int: "s + to_string(_data.a);
            case FLOAT:
                return "I am a float: "s + to_string(_data.b);
            case BOOLEAN:
                return "I am a boolean: "s + (_data.c ? "true"s : "false"s);
            case STRING:
                return "I am a string: "s + _data.d;
            case ERROR:
                return "I am an error: "s + _data.d;
        }
    }


};

int main()
{
    P p;
    std::cout << "p is " << p.report() << std::endl;

    auto x = P("hello");
    std::cout << "x is " << x.report() << std::endl;

    auto y = P("goodbye", as_error);
    std::cout << "y is " << y.report() << std::endl;

    auto z = P(4.4);
    std::cout << "z is " << z.report() << std::endl;


    return 0;
}

预期结果:

p is I am a boolean: false
x is I am a string: hello
y is I am an error: goodbye
z is I am a float: 4.400000

关于c++ - 将 char* 替换为 shared_ptr<char>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35983351/

相关文章:

c++ - 如果我需要多态性,我应该使用原始指针而不是 unique_ptr 吗?

c - 不寻常的指针声明

c++ - 将 shared_ptr<Type> 转换为 weak_ptr<void> 并返回

c++ - Botan AutoSeeded_RNG无法初始化,引用未知,尽管库已正确链接

c++ - 创建嵌套类的实例时,是否也创建了嵌套类的实例?

c - C语言 : pointer to integer conversion assigning to 'char' from 'char [2]'

c++ - 指向正确对象但未分配的 Shared_Ptr

c++ - 模板实例化深度超过最大值 - 如何停止特定情况?

c++ - 我怎样才能找出这个 ffmpeg 错误代码的含义?

c++ - 我能知道调用者是否保留了共享指针的拷贝吗?