简体   繁体   中英

C++ Get reference of object inside vector

Through a series of layers, I want to create an object inside a vector and then get the address to that object. This is producing a segment fault. I'm actually kind of stumped.. I am returning the address of the object created and should be setting the value of the pointer to that address.

#include <iostream>
#include <string>
#include <vector>

class Obj {
  public:
    std::string name;
};

class Container {
    public:
        Obj& CreateObj(std::string name);
        std::vector<Obj> objs;
};

Obj& Container::CreateObj(std::string name){
    Obj obj;
    obj.name = name;
    objs.push_back(obj);
    std::cout << "Container: 0x" << &objs.back() << std::endl; 
    return objs.back();
}

class App {
    public:
        Container container;
        void CreateObj(std::string name, Obj *&obj);
};
void App::CreateObj(std::string name, Obj *&obj){
    std::cout << "App: 0x" << obj << std::endl;
    *obj = container.CreateObj(name);       
    std::cout << "App: 0x" << obj << std::endl;
}

int main()
{
    Obj *obj = 0;
    std::cout << "Main: 0x" << obj << std::endl;
    App app;
    app.CreateObj("ok", obj);
    std::cout << "Main: 0x" << obj << std::endl;

    std::cout << obj->name;
}

I'm all for a "better" way to do this - the constraint is that I must retrieve that ptr/ref through a parameter.

I believe that the segmentation fault is occurring on the line

*obj = Container.CreateObj(name);

in the method App::CreateObj. The variable obj in main is a nullptr, which you then pass into 'Container.CreateObj'. When the line '*obj = Container.CreateObj(name);' executes, the program tries to copy construct the resulting object from 'Container.CreateObj(name)' to the memory at address 0, resulting in the segmentation fault. I think this is the way I would go about "fixing" the code, though I'm not exactly sure if this is what you want:

#include <vector>
#include <iostream>
#include <string>
#include <memory>

class Obj {
    public:
        std::string name;
};

class Container {
    public:
        Obj& CreateObj(std::string name);
        std::vector<Obj> objs;
};

Obj& Container::CreateObj(std::string name){
    Obj obj;
    obj.name = name;
    objs.push_back(obj);
    std::cout << "Container: 0x" << &objs.back() << std::endl;·
    return objs.back();
}

class App {
    public:
        Container container;
        void CreateObj(std::string name, Obj *obj);
};

void App::CreateObj(std::string name, Obj *obj){
    std::cout << "App: 0x" << obj << std::endl;
    *obj = container.CreateObj(name);
    std::cout << "App: 0x" << obj << std::endl;
}

int main()
{
    std::unique_ptr<Obj> obj = std::make_unique<Obj>();
    App app;
    app.CreateObj("ok", obj.get());

    std::cout << obj->name;
}

Another way you could tackle this is with std::variant . This would allow you to either return a reference to an Object (using reference_wrapper ) or an error code.

#include <variant>
#include <functional>

struct Object 
{  
    Object( std::string n ) 
        : name { std::move( n ) } { } 

    std::string name; 
};

using ObjectRef = std::reference_wrapper<Object>;
using ErrorCode = int;
using Either = std::variant<ErrorCode, ObjectRef>;

class Container 
{
public:
    Either CreateObject( std::string name );
    std::vector<Object> objects;
};

Either Container::CreateObject( std::string name )
{
    if ( name.compare( "bill" ) == 0 ) return -10;
    return objects.emplace_back( std::move( name ) );
}

class App 
{
public:
    Container container;
    Either CreateObject( std::string name );
};

Either App::CreateObject( std::string name )
{
    return container.CreateObject( std::move( name ) );
}

int main( )
{
    App app;
    auto either{ app.CreateObject( "karen" ) };
    if ( std::holds_alternative<ErrorCode>( either ) )
    {
        auto error{ std::get<ErrorCode>( either ) };
        std::cerr << "Failed to create karen. Error code: " << error << '\n';
        return error;
    }

    auto& karen{ std::get<ObjectRef>( either ).get( ) };
    std::cout << karen.name << '\n';

    either = app.CreateObject( "bill" );
    if ( std::holds_alternative<ErrorCode>( either ) )
    {
        auto error{ std::get<ErrorCode>( either ) };
        std::cerr << "Failed to create bill. Error code: " << error << '\n';
        return error;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM