Передача возвращаемого значения функции-члена класса по ссылке в C ++ - PullRequest
0 голосов
/ 20 мая 2011

Я искал решение этой проблемы, но, похоже, не могу его найти.Я уверен, что этот общий вопрос уже задавался где-то ранее, но, надеюсь, вы можете помочь с моей конкретной ситуацией ...

У меня есть шаблон класса someClass, который содержит следующие (частные) члены:

int     size_x;
int     size_y;
int     size_total;
T *     grid;

someClass содержит конструктор, который выглядит следующим образом:

someClass (const int x, const int y)
: size_x (x), size_y (y), size_total (x*y)
{
    grid = new T [size_total];
}

конструктор копирования, который выглядит следующим образом:

someClass (const someClass & rhs)
{
    size_x = rhs.size_x;
    size_y = rhs.size_y;
    size_total = rhs.size_total;
    grid = new T [size_total];
    memcpy(grid, rhs.grid, size_total*sizeof(T));
}

функция-член, которая выглядит следующим образом:

T * retGrid (void) const
{
    return grid;
}

и оператор присваивания, который выглядит следующим образом:

someClass & operator= (const someClass & rhs)
{
    if (this != &rhs)
    {
        size_x = rhs.size_x;
        size_y = rhs.size_y;
        size_total = rhs.size_total;
        grid = new T [size_total];
        memcpy(grid, rhs.grid, size_total*sizeof(T));
    }

    return *this;
}

Я пытаюсь передать следующие два someClass объекта

someClass<double> *I1 = new someClass<double>(10,10);

someClass<double> I2 = *I1;

для функции вне класса someClass со следующим прототипом:

int someFunction(double *arr);

Этот вызов работает нормально:

int status;
status = someFunction(I1->retGrid()); // Properly working function call

, но это не

status = someFunction(&I2.retGrid()); // Compiler gives error that says "error: invalid lvalue in unary &"

И если я вызываю someFunction следующим образом:

status = someFunction(I2.retGrid()); // Compiler gives no error but function returns error value in status

код компилируется, но я получаю ошибку во время выполнения (неверное значение состояния из-за другого вызова функции в someFunction).

Как правильно передать I2 в someFunction?

Большое спасибо ...

Ответы [ 5 ]

1 голос
/ 20 мая 2011

Первый вопрос: почему не используйте std::vector вместо того, чтобы пытаться управлять памятью самостоятельно.Вы не показываете деструктор;Я полагаю, вы освободите память там.Но есть еще проблемы:

  • В конструкторе копирования вы используете memcpy.Это не проблема, когда вы создаете экземпляр более double, но это может быть проблемой для других типов.Вы должны использовать std::copy.

  • Если вы используете std::vector, а retGrid все еще необходимо для возврата T*, это будет return &grid[0];.

  • Оператор присваивания не работает.Он теряет любую предыдущую память, а если new не удается, он оставляет объект в несогласованном состоянии.(Необходимость проверки на самостоятельное назначение обычно является намеком на то, что что-то не так.) Правильный оператор присваивания выполнит все операции, которые могут завершиться неудачей, прежде чем что-либо изменить в объекте.Вы можете искать информацию об идиоме подкачки, но что-то вроде этого также будет работать:

.

SomeClass&
SomeClass<T>::operator=( SomeClass const& other )
{
    T* newGrid = new T[other.size_total];
    std::copy( other.grid, other.grid + other.size_total, newGrid );
    delete [] grid;
    size_x = other.size_x;
    size_y = other.size_y;
    size_total = other.size_total;
    grid = newGrid;
    return *this;
}

Возможно, вы захотите оптимизировать это, если size_total равны (или size_total <= other.size_total).

И, конечно, если вы используете std::vector, достаточно сгенерированного компилятором оператора присваивания и конструктора копирования;вам не нужно ничего писать.

  • Есть ли причина, по которой вы используете указатель для I1?(Или это просто артефакт большего контекста, из которого вы извлекли код?)

  • Относительно someFunction( &I2.retGrid() );, someClass::retGrid() возвращает указатель.Независимо от того, вызываете ли вы функцию через указатель или объект.Взятие адреса приводит к T**.

  • Относительно последнего вызова, нет ничего, что могло бы вызвать проблему здесь в коде, который вы нам показали.Пока I2 находится в области видимости и не удалил свой объект, проблем не должно быть.Это правильный способ вызова функции для объекта.Проблема в вашем коде в другом месте.

1 голос
/ 20 мая 2011

Если у вас есть указатели в вашем классе, и вам нужно выделить память для каждого объекта, вам нужно определить конструктор копирования и оператор присваивания.Вы только добавили позже.Эта инициализация

someClass<double> I2 = *I1;

фактически выполняется конструктором копирования, а не оператором присваивания.Это идентично

someClass<double> I2(*I1);

Однако, это неправильно, потому что вы только выделяете память для сетки.Но если сетка уже была выделена (из предыдущего назначения), вы бы потеряли память.Так должно выглядеть так:

someClass & operator= (const someClass & rhs)
{
    if (this != &rhs)
    {
        size_x = rhs.size_x;
        size_y = rhs.size_y;
        size_total = rhs.size_total;
        delete [] grid;
        grid = new T [size_total];
        memcpy(grid, rhs.grid, size_total*sizeof(T));
    }

    return *this;
}
1 голос
/ 20 мая 2011

Вы пытаетесь получить адрес временного объекта (в данном случае указатель, но это на самом деле не имеет значения), возвращаемого retGrid.Следовательно, вы не можете использовать подход &.

Без амперсанда вы бы передали внутренний массив от I2 до someFunction.Если это вам не подходит (т. Е. Поскольку вы получаете какую-то ошибку времени выполнения), попробуйте сделать копию этого массива и вместо этого передать ее в someFunction.

0 голосов
/ 21 мая 2011

Спасибо всем за чрезвычайно полезные комментарии, особенно комментарии AAT, Дэвида Родригеса - Дрибея, Джеймса Канзе и Мариуса Банчилы.

Оказывается, проблема в неправильном интерфейсе со сторонней функцией внутри someFunction. Я нашел и исправил ошибку после некоторого сна и теперь звонил:

status = someFunction(I2.retGrid()); // Compiler gives no error but function returns error value in status

работает правильно.

Но это обсуждение выявило другие очень важные связанные с этим вопросы, а именно вопросы управления памятью, связанные с моим конструктором копирования и оператором присваивания, и предложение использования векторов. Я считаю, что эта тема имеет большое значение для этих достоинств.

0 голосов
/ 20 мая 2011

Я не уверен, что вы получаете ошибку времени выполнения, но status = someFunction(&I2.retGrid()); передает указатель на временный объект.

Ошибка во время выполнения может быть из-за отсутствия конструктора копирования, который вызывается в someClass<double> I2 = *I1;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...