質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

661閲覧

ポインタを戻り値として返す方法

tkhs314

総合スコア18

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/12/28 12:06

#戻り値でポインタを含むオブジェクトを返したい
C++で行列クラスの作成をしています.いま,operatorを用いて行列同士の加算を+で定義しようとしたところ,コンパイルは通ったのですが,実行結果が正常に動作しませんでした.operatorの戻り値にポインタが含まれているため正常に動作しないということはわかったのですが,どのように訂正すれば良いかわかりません.どなたか教えてください...

C++

1#include <stdio.h> 2#include <stdlib.h> 3#include <math.h> 4 5// 行列クラス 6class Matrix{ 7protected: 8 double *m_matrix; 9 int m_row; 10 int m_column; 11public: 12 Matrix(int R, int C); //行列のサイズを指定して零行列を作成 13 Matrix(int R, int C, double *V); //行列のサイズを指定して行列を作成 14 Matrix(Matrix &matrix); //引数に指定された行列と同じ値を持つ新しい行列を生成 15 ~Matrix(); //コンストラクタで確保した動的メモリを解放 16 int IsOK(); //行列が有効かどうか返す 17 void Show(); //格納されている行列を画面に表示する 18 Matrix operator+ (Matrix &mat1); //2つの行列を加算 19}; 20 21//行列のサイズを指定して零行列を作成 22Matrix::Matrix(int R, int C) : m_matrix(NULL) 23{ 24 if(R<1){ 25 fprintf(stderr,"行指定が不正です.\n"); 26 return ; 27 } 28 if(C<1){ 29 fprintf(stderr,"列指定が不正です.\n"); 30 return ; 31 } 32 33 m_row=R; 34 m_column=C; 35 m_matrix = new double [m_row*m_column]; 36 37 for(int i=0; i<m_row*m_column; i++){ 38 m_matrix[i]=0; 39 } 40 41} 42 43//行列のサイズを指定して行列を作成 44Matrix::Matrix(int R, int C, double *V) : Matrix(R, C) 45{ 46 if(m_matrix == NULL) return; 47 48 for (int i=0; i<R*C; i++){ 49 m_matrix[i]=V[i]; 50 } 51} 52//引数に指定された行列と同じ値を持つ新しい行列を生成(複製コンストラクタ) 53Matrix::Matrix(Matrix &matrix1):Matrix(matrix1.m_row,matrix1.m_column) 54{ 55 if (m_matrix == NULL) return; 56 if (matrix1.m_matrix == NULL) return ; 57 int N=matrix1.m_row*matrix1.m_column; 58 m_row=matrix1.m_row; 59 m_column=matrix1.m_column; 60 m_matrix=new double [N]; 61 for(int i=0; i<m_row*m_column; i++){ 62 m_matrix[i]=matrix1.m_matrix[i]; 63 } 64} 65//コンストラクタで確保した動的メモリを解放 66Matrix::~Matrix() 67{ 68 delete [] m_matrix; 69 m_matrix = NULL; 70} 71 72//行列がNULLかどうか返す 73//不正な計算が行われた際行列にNULLを代入している 74//行列がNULLの時画面表示しないようにIsOkをShow()関数内で呼び出す 75int Matrix::IsOK() 76{ 77 if (m_matrix == NULL) 78 return 0; 79 else 80 return 1; 81} 82//格納されている行列を画面に表示する 83//配列がNULLなら画面表示しないようにする 84void Matrix::Show() 85{ 86 if(IsOK() == 0){ 87 printf("ERROR\n"); 88 return; 89 } 90 for(int i=0; i<m_row; i++){ 91 printf("|"); 92 int j; 93 for(j=0; j<m_column; j++){ 94 printf("%6.2f",m_matrix[i*(m_column)+j]); 95 if(j<m_column-1) 96 printf(" "); 97 } 98 printf("|\n"); 99 } 100} 101 102 103//2つの行列を加算 104//エラー 105Matrix Matrix::operator+ (Matrix &mat1) 106{ 107 Matrix mat(m_row,m_column); 108 for(int i=0; i<m_row*m_column; i++){ 109 mat.m_matrix[i] = m_matrix[i]; 110 mat.m_matrix[i] += mat1.m_matrix[i]; 111 } 112 return mat; 113} 114 115int main() 116{ 117 double val1[]={1.0,2.0,3,0,4,0,5.0,6.0}; 118 Matrix mat1(2,3,val1); 119 printf("mat1=\n"); 120 mat1.Show(); 121 double val2[]={0.0,2.0,3.0,4,0,5.0,6.0}; 122 Matrix mat2(2,3,val2); 123 printf("mat2=\n"); 124 mat2.Show(); 125 Matrix mat3(mat1); 126 mat3=mat1+mat2; 127 printf("mat3=\n"); 128 mat1.Show(); 129}
実行結果:: mat1= | 1.00 2.00 3.00| | 0.00 4.00 0.00| mat2= | 0.00 2.00 3.00| | 4.00 0.00 5.00| mat3= | 1.00 2.00 3.00| | 0.00 4.00 0.00| *** Error in `': double free or corruption (fasttop): 0x000000000207bd20 *** ======= Backtrace: ========= ======= Memory map: ========

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

こんにちは。

ポインタの多重開放が発生していると思います。
Matrix Matrix::operator+ (Matrix &mat1)の中で mat を獲得していますが、これは文法上は operator+を抜けた時点で開放されます。従って、mat.m_matrixoperator+から抜ける時に~Matrixにてdelete[]されます。

そして、operator=を定義していないのに使っているため、このケースではコンパイラがoperator=(Matrix const&)を自動生成します。自動生成なのでそれ程高度なものではなく、単にメンバ変数同士を代入するだけです。m_matrixもmemcpy的にコピーされるのではなく単純にアドレス値が代入されるだけです。
ということは、main()関数のmat3=mat1+mat2;にて、mat.m_matrixmat3.m_matrixへ上書きされてしまいます。この時点で、元から有ったmat3.m_matrixが指す領域がリークします。
次に、mat3main()関数の終わりで開放されます。そこでもmat3.m_matrixdelete[]されますが、これは既にoperator+から抜ける時に開放されていますから多重開放となり、問題のエラーとなります。

対策はコピー代入演算子を定義すると良いです。

C++

1Matrix& operator=(Matrix const& matrix1) 2{ 3 int N=matrix1.m_row*matrix1.m_column; 4 m_row=matrix1.m_row; 5 m_column=matrix1.m_column; 6 m_matrix=new double [N]; 7 for(int i=0; i<m_row*m_column; i++){ 8 m_matrix[i]=matrix1.m_matrix[i]; 9 } 10 return *this; 11}

ここで、Matrix const& matrix1constが意外に重要なのでご注意下さい。
mat3=mat1+mat2;の右辺は一時オブジェクトを生成し、mat3operator=(Matrix const&)でそれをコピーします。しかし、Matrix& matrix1は一時オブジェクトを受け取れないとC++規格で決められていますので多くのコンパイラでエラーになります。(何故ダメ?って感じがしますし、実際ちょっと古いmsvcでは通りますので混乱しやすい部分です。)
constについては以前解説したことがありますので、良かったら参考にされて下さい。

ところで、最後にmat3=としてmat1を出力してますよ。
ハマってデバッグしたので、Show()とIsOk()にconstを補ってデバッグしやすいようにしました。

投稿2018/12/28 14:38

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tkhs314

2018/12/29 00:38

丁寧に解答してくださりありがとうございます! リンク先も読ませていただきました. 2点質問があります. 参照をつけると"ポインタのように戻り値を介さずに値を書き換えられるようにできる"という認識なのですが,それがconst と矛盾するような気がしてしまいます. 参照を用いるのはどのような時なのでしょうか? また,const& matとconst &matでは何か意味は変わってしまうのでしょうか?
Chironian

2018/12/29 07:20

参照やポインタをパラメータとして渡す目的はもう一つ、コピーしないで大量のデータを渡すことがあります。そして、受け取った側がそのパラメータの中身を修正しないのであれば、修正しないことをコンパイラに教え、間違って修正したらコンパイラに教えて貰うという使い方ができます。これによりバグが減り、かつ、テスト工数も減ります。 const& matとconst &matは同じです。私は型を示すという雰囲気がでるので「Matrix const&」の方を好みますが、この辺は好みの問題と思います。
guest

0

c++

1 Matrix mat3(mat1); 2 mat3=mat1+mat2;

これを実現するためにはoperator=を再定義し
mat3.m_matrixを解放し、(mat1+mat2).m_matrixの内容をコピーする必要があります。

こんな感じ

c++

1Matrix& Matrix::operator=(const Matrix &other){ 2 delete[] m_matrix; 3 m_row = other.m_row; 4 m_column = other.m_column; 5 m_matrix = new double[m_row * m_column]; 6 for(int i=0; i<m_row*m_column; i++){ 7 m_matrix[i]=other.m_matrix[i]; 8 } 9 return *this; 10}

もしくはさらに、ムーブ代入を追記しておくと
コピーが不要になり効率がよくなります

c++

1Matrix& Matrix::operator=(Matrix &&other){ 2 delete[] m_matrix; 3 m_row = other.m_row; 4 m_column = other.m_column; 5 m_matrix = other.m_matrix; 6 other.m_matrix = nullptr; 7 return *this; 8}

投稿2018/12/28 13:08

編集2018/12/28 14:16
asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問