Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Вернуть объект класса


Автор: efpies 24.11.2010, 15:07
Здравствуйте. Столкнулся с такой проблемой: нужно как-то вернуть объект класса.

вот, что есть

Код

// Конструктор копирования
Matrix::Matrix (const Matrix& X)
{
    Create  (X.rows_, X.cols_);

    for (unsigned i = 0; i < this->rows_; i++)
        for (unsigned j = 0; j < this->cols_; j++)
            this->values_[i][j] = X.values_[i][j];
}

// Транспонирование
Matrix& Matrix::Transpose()
{
    Matrix* temp = new Matrix(*this);

    temp->TransposePermanent();

    return *temp;
}

// Транспонирование
void Matrix::TransposePermanent()
{
    double** new_values = new double*[cols_];

    for (unsigned i = 0; i < cols_; i++)
    {
        new_values[i] = new double[rows_];

        for (unsigned j = 0; j < rows_; j++)
            new_values[i][j] = values_[j][i];
    }

    Erase ();

    unsigned t = rows_;
    rows_ = cols_;
    cols_ = t;

    values_ = new_values;
}

void main ()
{
    Matrix  c(3,2);
    Matrix *d;

    c.Print();

    *d = c.Transpose();
    d->Print();
}

При попытке вывода - Access violation, да и в значениях rows_ и cols_ мусор. Внутри Transpose всё выводит нормально, да и конструктор копирования вызывается.

То ли значение возвращает Transpose? Как быть, если надо вернуть объект и присвоить его адрес указателю?

Автор: triclosan 24.11.2010, 15:18
имхо как-то плохо выглядит.

если так 
Код


int main ()
{
    Matrix  c(3,2);
    Matrix *d = new Matrix();
    c.Print();
    *d = c.Transpose();
    d->Print();
    return 0;

}



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

Автор: efpies 24.11.2010, 15:56
Вопрос в удобстве: ведь проще объявить указатель в начале функции и потом его задействовать (хотя это плохо, говорят), чем объявить статичный объект и тут же присвоить. Может, я и ошибаюсь, конечно.

Ваша конструкция не сработала, зато сработала вот такая:
Код

Matrix *d;
d = &(c.Transpose());

Почему это так? Я разве не возвращаю Matrix&?

Автор: triclosan 24.11.2010, 16:06
Matrix& Matrix::Transpose() возвращает объект по ссылке.

В вашем коде получаете объект по ссылке, далее берете его адрес и пишите в указатель. 

Автор: bsa 24.11.2010, 16:17
efpies, не надо возвращать таким образом. потому что совсем не очевидно, что после этого необходимо присвоить адрес результата указателю, а в конце работы сделать delete. лучше сделай так:
Код
Matrix Matrix::Transpose()
{
    Matrix temp(*this);
    temp.ransposePermanent();
    return temp;
}
Утечек памяти хотя бы не будет.

Кстати, а метод Erase у тебя rows_ и cols_ случаем не обнуляет?

Автор: WhKitten 24.11.2010, 16:19
Цитата(efpies @ 24.11.2010,  15:56)
Ваша конструкция не сработала, зато сработала вот такая:
Код

Matrix *d;
d = &(c.Transpose());

Почему это так? Я разве не возвращаю Matrix&?

Помоему Вы путает ссылки и указатели это разные вещи.
Код

Matrix *d; // неинициализированный указатель на Matrix
Matrix &d = c.Transpose(); // ссылка на Matrix
 
поэтому код:
Код

Matrix *d;
*d = c.Transpose();

Трактуется как: выполнить оператор присваивания для объекта Matrix по адресу d. Поскольку d неинициализированна, то это вызывает Access Violation. Мы пытаемся выполнить оператор присваивания для несуществующего объекта класса.
Кстати, если бы Вы написали так:
Код

Matrix *d = c.Transpose();

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

Автор: bsa 24.11.2010, 16:26
Цитата(WhKitten @  24.11.2010,  17:19 Найти цитируемый пост)
выполнить оператор присваивания для объекта Matrix по адресу d.

Ну ты сказал...
На самом деле все проще: "присвоить объекту, находящемуся в ячейке памяти, на которую указывает d, значение возвращенное c.Transpose()". Под словом "присвоить" имеется в виду "вызвать оператор присваивания".

Автор: efpies 24.11.2010, 16:52
Цитата(bsa @  24.11.2010,  16:17 Найти цитируемый пост)
Кстати, а метод Erase у тебя rows_ и cols_ случаем не обнуляет?

Нет, он просто удаляет double** values и присваивает ему NULL

Цитата(bsa @  24.11.2010,  16:17 Найти цитируемый пост)
лучше сделай так:

А что присваивать *d в таком случае? Если &(c.Transpose()), то Access violation на выводе, но rows_ и cols_ в порядке.

Автор: xvr 24.11.2010, 17:40
Если вам нужно вернуть объект и присвоить его в переменную, то так и пишите:
Код

Matrix Matrix::Transpose()
{
    Matrix temp(*this);
    temp.TransposePermanent();
    return temp;
}

void main ()
{
    Matrix  c(3,2);
    Matrix d;
    c.Print();
    d = c.Transpose();
    d.Print();
}
И не надо накручивать вокруг всяких закорючек (* и &), смысла которых не понимаете  smile 

Автор: WhKitten 24.11.2010, 17:52
Цитата(xvr @ 24.11.2010,  17:40)
Если вам нужно вернуть объект и присвоить его в переменную, то так и пишите

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

Matrix& Matrix::operator= (const Matrix& obj)
{
    if (&obj != this) {
        // очистка
        // присвоение нового значения
    }
    return *this;
}


Но мне кажется проблема уже в чём то другом. &(c.Transpose()) возвращает адрес переменной в куче, а если delete не вызывалось, то она должна быть проинициализирована, о чём и говорит, что rows_ и cols_ в порядке. Может во время выполнения d->Print() выполняется доступ к несуществующему индексу?

Автор: WhKitten 24.11.2010, 18:14
И ещё вариант. Вместо:
Код

 double** values_

использовать:
Код

  std::vector<std::vector<double>> values_;

тогда отпадёт надобность и в операторе присваивания и в копирующем конструкторе. И с указателями путаницы будет меньше.

Автор: efpies 24.11.2010, 19:06
Цитата(xvr @  24.11.2010,  17:40 Найти цитируемый пост)
И не надо накручивать вокруг всяких закорючек (* и &), смысла которых не понимаете

Ну, возможно, стоит попробовать научиться понимать их смысл, не так ли? Операция & - взять адрес, * - взять по адресу? В то же время & - ссылка, а * - указатель? Нет?

Цитата(xvr @  24.11.2010,  17:40 Найти цитируемый пост)
Matrix Matrix::Transpose(){    Matrix temp(*this);    temp.TransposePermanent();    return temp;}

Да и вообще говоря, перегруженное сложение не вызовется, если не возвращать Matrix&, ибо not implemented.

Цитата(WhKitten @  24.11.2010,  17:52 Найти цитируемый пост)
о чём и говорит, что rows_ и cols_ в порядке

На самом деле, они были не в порядке, это моя ошибка: они становились равными 0.

Цитата(WhKitten @  24.11.2010,  17:52 Найти цитируемый пост)
Только тогда надо будет определить оператор присваивания

Оператор присваивания перегружен. Другое дело, что он вообще не вызывается, а вместо него вызывается конструктор копирования. Да и нужен ли этот оператор теперь? Что-то из них, выходит, можно убрать.

В итоге получается вот что:
1. Транспонирование проходит на "ура".
2. Копирование не работает. Копируется 0, вернее. Потому что вызывается деструктор. Не оттого ли, что в Transpose объект локален? Хотя это может быть бредом.

Цитата(WhKitten @  24.11.2010,  18:14 Найти цитируемый пост)
std::vector

До этого, видимо, пока рановато)

Автор: WhKitten 24.11.2010, 19:35
Цитата(efpies @ 24.11.2010,  19:06)
Цитата(xvr @  24.11.2010,  17:40 Найти цитируемый пост)
И не надо накручивать вокруг всяких закорючек (* и &), смысла которых не понимаете

Ну, возможно, стоит попробовать научиться понимать их смысл, не так ли? Операция & - взять адрес, * - взять по адресу? В то же время & - ссылка, а * - указатель? Нет?

Только надо отличать, когда это & объявление ссылки, а когда взятие адреса.
Код

Matrix& func1() { static Matrix x1; return x1; } // возвращает ссылку на x1
Matrix* func2() { static Matrix x2; return &x2; } // возвращает указатель на x2
Matrix &y = func1(); // y ссылка на x1
Matrix *z = *func1(); // ОШИБКА: для Matrix не определён оператор *
Matrix *z = &func1(); // z указатель на x1
*z = func2(); // ОШИБКА: нельзя преобразовать Matrix* в Matrix
z = func2(); // ОК, z указатель на x2
z = &func1(); // z указатель на x1
*z = *func2(); // присвоить x1 значение x2
z = y; // ОШИБКА: нельзя преобразовать Matrix в Matrix*
*z = y; // OK

Автор: triclosan 24.11.2010, 19:45
Цитата(efpies @  24.11.2010,  19:06 Найти цитируемый пост)
До этого, видимо, пока рановато) 

Вы не правы, как раз в этом направлении стоит углублять изучение С++, а мрачных конструкций new - delete по возможности пока избегать, вы же не контроллеры программируете. Пусть вас не вводит в заблуждение, что во многих книгах по с++ для новичков new - delete рассматривается как полноценный инструмент, всему свое время. Для правильного и глубокого понимания std::* могу порекомендовать 
Николай Джосьютис, C++. Стандартная библиотека, Питер, 2004.

Автор: xvr 24.11.2010, 20:50
Цитата(efpies @  24.11.2010,  19:06 Найти цитируемый пост)
Оператор присваивания перегружен. Другое дело, что он вообще не вызывается, а вместо него вызывается конструктор копирования. Да и нужен ли этот оператор теперь? Что-то из них, выходит, можно убрать.
Нужны оба. Они должны делать приблизительно одно и тоже, а заменить их кем то одним увы не получится - они вызываются в разных случаях (и в обоих случаях надо делать корректное копирование)


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)