Как передать std :: string в качестве аргумента конструктора и сохранить строку C в указателе void? - PullRequest
0 голосов
/ 12 июня 2018

У меня есть конструктор, который принимает ссылку const на объект std::string и должен хранить внутреннюю строку C std::string в переменной-члене-указателе void.Тем не менее, моя программа вызывает ошибки при использовании указателя void, а gdb говорит, что указатель указывает на адрес 0x01.Вот мой источник:

Foo.h:

class Foo {
    public:
        Foo(const std::string& str);
        void* getData();

    private:
        Foo(void* newData);
        void* data;
};

Foo.cpp:

Foo::Foo(const std::string& str)
    : Foo(str.c_str())
{
    //nothing
}

void* Foo::getData() {
    return data;
}

Foo::Foo(void* newData)
    : data(newData)
{
    //nothing
}

main.cpp:

int func(void* data);

int main() {
    Foo f("bar");

    func(f.getData()); //segfault here

    return 0;
}

int func(void* data) {
    std::string str = (char*)data;
    std::cout << str << std::endl;

    return 0;
}

Ответы [ 4 ]

0 голосов
/ 12 июня 2018

Во-первых, я бы порекомендовал не использовать void * Есть очень немного ситуаций, когда вам нужно использовать void *, и вы потеряете любую полезную информацию о типе.

Если вы хотите манипулировать битами непосредственно в std:: string object, затем из C ++ 17, метод data on string предоставляет доступ к символам напрямую, не будучи константой.

Если вы хотите сделать свой код более безопасным, просто сохраните std :: string в качестве членапеременной вашего класса Foo, и используйте доступные вам методы.Это также означает, что вам не нужно будет вручную отслеживать внутренний указатель строки, передаваемой из c_str или вызовов метода данных.

Например (частичный пример)

class Foo {
public:
    Foo(const std::string& str);
    const char* getData() const;
    char* getData();

private:
    std::string m_str;
};

Foo::Foo(const std::string& str)
    : m_str(str)
{
}

const char* Foo::getData() const
{
    return m_str.c_str();
}

char* Foo::getData()
{
    // Note: Need C++ 17
    return m_str.data();
}
0 голосов
/ 12 июня 2018

Foo f ("bar");

Вы передаете ссылку на временное значение в конструктор.Время жизни объекта только внутри конструктора.Таким образом, данные void * указывают на удаленную память.

Чтобы быть более точным, если вы не в курсе, вы можете узнать разницу между l-значением и r-значение в C ++.

например) int a = 5

Здесь a - это l-значениеи 5 - значение r.Проще говоря, они являются категорией значений типа данных.

https://en.cppreference.com/w/cpp/language/value_category

Никогда не следует хранить ссылку на r-значение.Всегда проверяйте срок службы объекта перед выполнением какой-либо операции и убедитесь, что объект остается в живых до завершения операции.

Решение

string a = "bar";
Foo foo(a);

Здесь a - это l-значение.Но пользователь должен убедиться, что срок жизни a превышает foo .

Или лучше

string a = "bar";
{
Foo foo(a);
}

Таким образом вы гарантируете, что foo будет уничтожено до a

0 голосов
/ 12 июня 2018

Причина, по которой ваш код аварийно завершает работу, заключается в том, что

: Foo(str.c_str())

вызывает

Foo(const std::string& str);

, а не

Foo(void* newData);

, поэтому вы имеете бесконечную рекурсию.Это все плохо, и я советую вам переосмыслить свой подход, однако просто для того, чтобы запустить его, приведите str.c_str() к void * явно:

: Foo((void *)str.c_str())

И, конечно, сделайте «бар» самостоятельным.string, чтобы избежать работы с временными файлами и передать строку в конструктор.Таким образом, указатель будет действителен до тех пор, пока не закончится область с вашим string.

Демо: https://ideone.com/v7YfBX

0 голосов
/ 12 июня 2018

Строка, составленная из "bar", больше не существует при вызове f.getData(), поэтому указатель на ее внутренний массив символов больше не действителен.Строка только что создана для вызова функции и снова уничтожается перед выполнением остальной функции main.Segfault происходит, когда вы разыменовываете неверный указатель.

Вместо этого попробуйте следующее:

const std::string str("bar");
Foo f(str);

Таким образом, строка все еще находится в области действия, когда необходимы данные.


Кроме того, общий подход не рекомендуется.Лучше не использовать void* указатели и необработанные указатели, если это возможно, или, по крайней мере, безопасно управлять ими внутри объекта.Ваша проблема уже дает понять, почему использование указателей такого типа небезопасно и подвержено ошибкам.

...