C++ 将char*替换为shared_ptr<;char>;

C++ 将char*替换为shared_ptr<;char>;,c++,pointers,shared-ptr,C++,Pointers,Shared Ptr,我有一个结构如下: struct P { enum {INT, FLOAT, BOOLEAN, STRING, ERROR} tag; union { int a; float b; bool c; const char* d; }; }; 我正在使用谷物库来序列化它,谷物不支持原始指针。我正在用constshared\u ptr d替换constchar*d。我面临三个问题: 将字符*转换为共享的

我有一个结构如下:

struct P
{
    enum {INT, FLOAT, BOOLEAN, STRING, ERROR} tag;
    union
    {
        int a;
        float b;
        bool c;
        const char* d;
    };
};
我正在使用谷物库来序列化它,谷物不支持原始指针。我正在用
constshared\u ptr d
替换
constchar*d
。我面临三个问题:

  • 将字符*转换为共享的\u ptr:

    char* x = //first element of char array
    d = shared_ptr<char> (x); // is this the right way?
    
    char*x=//char数组的第一个元素
    d=共享_ptr(x);//这条路对吗?
    
  • 处理任务,如:

    string s = "hello";
    d = s.c_str(); // how to convert the c_str() to shared_ptr<char>?
    
    string s=“你好”;
    d=s.c_str();//如何将c_str()转换为共享_ptr?
    
  • 据我所知,shared_ptr处理的指针似乎与原始指针非常不同。我是否能够安全地将此共享的_ptr用作角色数组而不产生任何副作用


  • 首先要说的是你在使用工会。C++中的联合体很难正确。你真的需要工会吗

    如果您确实需要一个联合体,请改用
    boost::variant
    。它为您解决了所有的复杂性

    接下来,我们使用C++而不是C。去掉那个常量字符*。这是地雷。这就是为什么谷物不支持它。他们在做正确的事情。换成现在的样子。A

    std::string

    编辑:

    嗯。你自找的。下面是一个使用歧视性联盟的解决方案

    现在,我记得工会在C++中很难正确的处理吗?p> 在过去的15年(20年)里,我几乎每天都在写C++。我是标准进步的狂热追随者,我总是使用最新的工具,我要求团队中的人彻底了解语言和标准库。。。我还不确定这个解决方案是否完全可靠。我需要花一天的时间写测试来确定。。。因为受歧视的工会很难纠正

    编辑2:

    修复了“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;
    }
    

    根据问题标题的要求,将
    char*
    替换为
    shared\u ptr
    ,可以进行编译(在自定义删除程序的帮助下),但这几乎没有任何用处,并且几乎肯定不会在谷物的上下文中做正确的事情。既然Groove理解字符串等标准库类型,为什么不直接使用它们呢

    由于联合和非POD类型不会真正混合,Richard对此进行了恰当的演示,因此可以使用虚拟成员函数将联合转换为继承类,以区分类型:

    struct P {
      enum tag_type {INT, FLOAT, BOOLEAN, STRING, ERROR };
      virtual tag_type get_tag() const = 0;
      // allow subclsas deletion through base class pointer
      virtual ~P() {}
    };
    struct PInt: public P {
      tag_type get_tag() { return INT; }
      int a;
    };
    struct PFloat: public P {
      tag_type get_tag() { return FLOAT; }
      float b;
    };
    struct PBool: public P {
      tag_type get_tag() { return BOOL; }
      bool c;
    };
    struct PStr: public P {
      tag_type get_tag() { return STRING; }
      std::string d;
    };
    struct PError: public P {
      tag_type get_tag() { return ERROR; }
    };
    
    最终得到的代码仍然很笨拙,但它可以轻松、稳健地处理非POD。使用它可以归结为替换如下所示的旧代码:

    void process(P& x)
      switch (x.tag) {
        case INT:
          // use x._data.a
          ...
      }
    }
    
    …代码使用
    get_tag()
    virtual成员获取标记,并使用
    dynamic_cast
    访问最终类的属性:

    void process(P& x)
    {
      switch (x.get_tag()) {
        case P::INT:
          PInt &intx = dynamic_cast<PInt&>(x);
          // ...use intx.a
        // case P::FLOAT, etc.
      }
    }
    
    作废流程(P&x)
    {
    开关(x.get_标记()){
    案例P::INT:
    品脱&intx=动态铸型(x);
    //…使用intx.a
    //案例P:浮动等。
    }
    }
    

    使用此设置,您可以使用,将您的子类声明为。然后,库将使用运行时类型信息来正确序列化指向
    P

    的指针。看起来您的原始指针是非所有者的,因此您可能会双重删除某些内容。除了使用
    std::shared\u ptr
    ,您的库还有其他选择吗?您是否建议改用unique\u ptr?我正在使用shared_ptr,因为它不会导致所有权冲突。您可以使用
    std::shared_ptr
    ?除非您试图序列化字符,
    std::shared\u ptr
    将无法工作。可能
    std::shared\u ptr
    ?1。这是错误的方式;2.它没有这样做,也许你可以使用一个
    std::shared_ptr
    和一个自定义的no-op-deleter?这更有意义,感谢你指出了在标记的联合中使用字符串的正确方法。这仍然会给出:
    error:union成员'P:::d'和非平凡的'std::basic_string&std::basic_string::operator=(const std::basic_string&)[带_CharT=char;_Traits=std::char_Traits;_Alloc=std::allocator]'std::string d;
    我使用的是g++-std=C++0x,但这个错误也表明结构定义的格式不正确。我正确地添加了构造函数和析构函数。我哪里出错了?就像我说的,工会很难。我现在正从马德里开车去巴塞罗那,但如果你能等的话,今晚可以再次访问。当然,感谢你的帮助.
    void process(P& x)
    {
      switch (x.get_tag()) {
        case P::INT:
          PInt &intx = dynamic_cast<PInt&>(x);
          // ...use intx.a
        // case P::FLOAT, etc.
      }
    }