ページ 1 / 1
C2662エラーについて
Posted: 2012年3月06日(火) 00:44
by MoNoQLoREATOR
このページによりますと、C2662エラーは、関数定義の際 { の前に const を書くことによって解決できるそうですが(以前もその方法で解決したことがあります)、処置後も同じエラーが出続けます。引数の const を取れば解決しそうですが、できれば const のままにしておきたいところです。
以下ソースコード、エラー文、仕様書です。
ちなみに内容は自作のvectorクラスとなっております。
ソースコード
► スポイラーを表示
コード:
#define _CRT_SECURE_NO_DEPRECATE
#pragma once
namespace fw{
typedef unsigned int uint;
template<typename X>
class vector{
X * cont;
uint Size;
uint Space;
int Perm;
public:
vector(){
cont = new X[256];
Size = 0;
Space = 256;
Perm = false;
}
uint size(){ return Size; }
fw::vector<X> & resize(uint size){
if(size > Size){
if(size > Space){
X * ins = new X[Size];
memcpy(ins, cont, sizeof(X)*Size);
delete [] cont;
cont = new X[size+256];
memcpy(cont, ins, sizeof(X)*Size);
delete [] ins;
Space = size+256;
}
for(uint i=Size;i<size;i++) cont[i] = X();
Size = size;
}
return *this;
}
fw::vector<X> & addsize(uint size){
resize(size+Size);
return *this;
}
fw::vector<X> & operator= (const std::vector<X> & sv){
resize(sv.size() );
Size = sv.size();
for(uint i=0;i<sv.size();i++) cont[i] = sv[i];
return *this;
}
X at(uint index){
uint needSize = index+1;
if(needSize > Size){
if(Perm){
if(Perm==-1) resize(needSize);
else{
if(needSize<=(uint)Perm) resize(needSize);
else delete [] cont;
}
}
else delete [] cont;
}
return cont[index];
}
X & operator[] (uint index){
at(index);
return cont[index];
}
fw::vector<X> & pop(){
Size--;
return *this;
}
fw::vector<X> & add(){
addsize(1);
return *this;
}
fw::vector<X> & add(const X & input){
addsize(1);
cont[Size] = input;
return *this;
}
fw::vector<X> & add(const fw::vector<X> & input) const {
for(uint i=0;i<input.size();i++){
perm();
cont[size()] = input.at(i);
lim();
}
return *this;
}
fw::vector<X> & operator++ (){
add();
return *this;
}
fw::vector<X> operator++ (int){
fw::vector<X> send = *this;
add();
return send;
}
fw::vector<X> & operator+= (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator+= (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> operator+ (const X & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> operator+ (const fw::vector<X> & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> & perm(){
Perm = -1;
return *this;
}
fw::vector<X> & perm(uint limit){
Perm = limit;
return *this;
}
fw::vector<X> & lim(){
Perm = 0;
return *this;
}
~vector(){
delete [] cont;
}
};
}
エラー文
► スポイラーを表示
コード:
------ ビルド開始: プロジェクト: fireworks, 構成: Debug Win32 ------
コンパイルしています...
fireworks.cpp
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\hs\vector.h(94) : error C2662: 'fw::vector<X>::size' : 'const fw::vector<X>' から 'fw::vector<X> &' へ 'this' ポインタを変換できません。
with
[
X=std::string
]
変換で修飾子が失われます。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\hs\vector.h(93): クラス テンプレート のメンバ関数 'fw::vector<X> &fw::vector<X>::add(const fw::vector<X> &) const' のコンパイル中
with
[
X=std::string
]
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\hs\path.h(33) : コンパイルされたクラスの テンプレート のインスタンス化 'fw::vector<X>' の参照を確認してください
with
[
X=std::string
]
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\hs\vector.h(95) : error C2663: 'fw::vector<X>::perm' : 2 オーバーロードに 'this' ポインタのための必要な定義がされていません。
with
[
X=std::string
]
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\hs\vector.h(96) : error C2662: 'fw::vector<X>::size' : 'const fw::vector<X>' から 'fw::vector<X> &' へ 'this' ポインタを変換できません。
with
[
X=std::string
]
変換で修飾子が失われます。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\hs\vector.h(96) : error C2662: 'fw::vector<X>::at' : 'const fw::vector<X>' から 'fw::vector<X> &' へ 'this' ポインタを変換できません。
with
[
X=std::string
]
変換で修飾子が失われます。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\hs\vector.h(97) : error C2662: 'fw::vector<X>::lim' : 'const fw::vector<X>' から 'fw::vector<X> &' へ 'this' ポインタを変換できません。
with
[
X=std::string
]
変換で修飾子が失われます。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\hs\vector.h(99) : error C2440: 'return' : 'const fw::vector<X>' から 'fw::vector<X> &' に変換できません。
with
[
X=std::string
]
変換で修飾子が失われます。
ビルドログは "file://c:\Documents and Settings\Administrator\My Documents\Visual Studio 2008\Projects\fireworks\Debug\BuildLog.htm" に保存されました。
fireworks - エラー 6、警告 0
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========
仕様書
► スポイラーを表示
コード:
基本はstd::vectorと同じである。
ユーザーは、resize(),addsize(),add()などの関数によって容量を確保し、添字によって要素にアクセスするという理解で良い。
要素にアクセスする際、そのインデックス番号がユーザーの確保した容量を超えており、かつ自動拡張が許可されていない場合はアプリケーションエラー(範囲外アクセスエラー)が発生する。
尚、全ての要素が連続的かつインデックス番号順にメモリに格納されていることが保証されている。
つまり、インデックス番号0番目の要素が示すアドレスを先頭アドレスとし、通常の配列のように扱うことができる。
~メンバ関数一覧~
●オリジナル
コンストラクタ・・・引数はない
fw::vector<X> & operator= (const std::vector<X> & sv) ・・・ 本家を代入することもできる。
fw::vector<X> & perm(uint size) :
perm は permit の略で"許可する"の意。
現在の容量+指定した容量までの自動拡張が許可される。
自動拡張が許可されている場合、添字によってアクセスした際に、必要なら自動で新しい容量が確保される。
引数は省略することができ、その場合無制限に自動拡張が許可される。
ちなみにデフォルトでは許可されていない。
fw::vector<X> & lim() ・・・ lim は limit の略で"制限する"の意。自動拡張を許可しない設定にする。
fw::vector<X> & addsize(uint size) ・・・ 内部で resize(size()+size) が行われる。
fw::vector<X> & pop() ・・・ 本家pop_back()に同じ。
fw::vector<X> & add() ・・・ 要素1つ分の容量が新しく確保される。領域は要素のデフォルトコンストラクタで初期化される。
fw::vector<X> & add(const X & input) ・・・ 本家push_back()に同じ。
fw::vector<X> & add(const fw::vector<X> & input) ・・・ 指定したvectorの内容が追加される。容量も必要な分確保される。
fw::vector<X> & operator++ () ・・・ add() に同じ。
fw::vector<X> operator++ (int) ・・・ add() に同じ。ただし、返値は add() を行う前のデータである。
fw::vector<X> & operator+= (const X & input) ・・・ add(const X & input) に同じ。
fw::vector<X> & operator+= (const fw::vector<X> & input) ・・・ add(const fw::vector<X> & input) に同じ。
fw::vector<X> & operator<< (const X & input) ・・・ add(const X & input) に同じ。
fw::vector<X> & operator<< (const fw::vector<X> & input) ・・・ add(const fw::vector<X> & input) に同じ。
fw::vector<X> operator+ (const X & target) ・・・ 自分のデータの後に target のデータが追加されたデータが返される。
fw::vector<X> operator+ (const fw::vector<X> & target) ・・・ 自分のデータの後に target のデータが追加されたデータが返される。
●本家と同様
size()
resize()
operator[]
at() //※ただし値が返されるだけ
エラーの原因が私には全くわかりません。
ご教授よろしくお願い致します。
Re: C2662エラーについて
Posted: 2012年3月06日(火) 11:23
by YuO
const型のオブジェクトに対しては,constとされた関数しか呼び出せません。
「呼び出し元の関数がconst」ではなく,「呼び出す関数がconst」です。
今回の最初のエラーについていうならば,25行目の
MoNoQLoREATOR さんが書きました:コード:
uint size(){ return Size; }
が
コード:
uint size() const { return Size; }
でないといけません。そして,93行目の
MoNoQLoREATOR さんが書きました:コード:
fw::vector<X> & add(const fw::vector<X> & input) const {
は,constである必要はありません。それどころか,addはオブジェクトの状態を変更する操作ですからconst修飾してはいけません。
# const修飾された関数の呼び出しでは,少なくとも外部から見たオブジェクトの状態は変化しないことが期待されます。
なお,const付きと非const付きはオーバーロードできます。
このクラス自体にもいろいろ問題があるのですが……。
# ぱっと見ただけでも,memcpyがあるので非PODで使うには危険,resizeで無駄にコンストラクタとデストラクタが走る,atでデータ構造が壊れる可能性がある
Re: C2662エラーについて
Posted: 2012年3月06日(火) 15:38
by MoNoQLoREATOR
ありがとうございます。エラーは出なくなりました。
クラス自体に問題があるというのならば、直したいです。
たしかに、ちょいとリスキーな動きだな という自覚はありますが^^;
自覚があるのは、おかしな場所でdeleteしている点です。
「ユーザー視点において確保された容量」を超える要素にアクセスしてしまう事態が起こった場合は、範囲外アクセスエラーを出すべきだと思うわけですが、内部では「ユーザー視点において確保された容量」よりも多めに容量が確保されている状態であり、ほとんどの場合、自動で範囲外アクセスエラーが出ません。そこで、そのような事態が起こった場合は、確保した容量をdeleteしてしまうことによって強制的に範囲外アクセスエラーが出るようにしています。やはりtry~catch文を書いたほうがよいのでしょうか?
YuOさんに指摘していただいた点については疑問しか出てきません。
>>memcpyがあるので非PODで使うには危険
PODという言葉が出てきたということは、ファイルに出力したりmemcpyを用いてデータをコピーしたりする場面でのお話ですよね?要素群のデータをファイルに出力したりmemcpyを用いてコピーしたりすることについてはなんら危険性はないはずです。このクラス自体に対してそのような処理をする場合は問題が出るかもしれませんが、通常そのようなことをすることはないでしょう。
>>resizeで無駄にコンストラクタとデストラクタが走る
私にはコンストラクタやデストラクタが走っているようには見えません。
>>atでデータ構造が壊れる可能性がある
デリートしている部分のことでしょうか?容量が増える場合があるのは仕様です。
おそらく私の知識が足りないものと思われます。
ご教授よろしくお願い致します。
P.S.
少しソースコードを変更しました。57行目辺りです。
► スポイラーを表示
コード:
#define _CRT_SECURE_NO_DEPRECATE
#pragma once
namespace fw{
typedef unsigned int uint;
template<typename X>
class vector{
X * cont;
uint Size;
uint Space;
int Perm;
public:
vector(){
cont = new X[256];
Size = 0;
Space = 256;
Perm = false;
}
uint size() const { return Size; }
fw::vector<X> & resize(uint size){
if(size > Size){
if(size > Space){
X * ins = new X[Size];
memcpy(ins, cont, sizeof(X)*Size);
delete [] cont;
cont = new X[size+256];
memcpy(cont, ins, sizeof(X)*Size);
delete [] ins;
Space = size+256;
}
for(uint i=Size;i<size;i++) cont[i] = X();
Size = size;
}
return *this;
}
fw::vector<X> & addsize(uint size){
resize(size+Size);
return *this;
}
fw::vector<X> & operator= (const std::vector<X> & sv){
resize(sv.size() );
Size = sv.size();
for(uint i=0;i<sv.size();i++) cont[i] = sv[i];
return *this;
}
X show(uint index) const {
uint needSize = index+1;
if(needSize > Size) delete [] cont;
return cont[index];
}
X & operator[] (uint index){
uint needSize = index+1;
if(needSize > Size){
if(Perm){
if(Perm==-1) resize(needSize);
else{
if(needSize<=(uint)Perm) resize(needSize);
else delete [] cont;
}
}
else delete [] cont;
}
return cont[index];
}
fw::vector<X> & pop(){
Size--;
return *this;
}
fw::vector<X> & add(){
addsize(1);
return *this;
}
fw::vector<X> & add(const X & input){
addsize(1);
cont[Size] = input;
return *this;
}
fw::vector<X> & add(const fw::vector<X> & input){
for(uint i=0;i<input.size();i++){
perm();
cont[size()] = input.show(i);
lim();
}
return *this;
}
fw::vector<X> & operator++ (){
add();
return *this;
}
fw::vector<X> operator++ (int){
fw::vector<X> send = *this;
add();
return send;
}
fw::vector<X> & operator+= (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator+= (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> operator+ (const X & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> operator+ (const fw::vector<X> & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> & perm(){
Perm = -1;
return *this;
}
fw::vector<X> & perm(uint limit){
Perm = limit;
return *this;
}
fw::vector<X> & lim(){
Perm = 0;
return *this;
}
~vector(){
delete [] cont;
}
};
}
Re: C2662エラーについて
Posted: 2012年3月06日(火) 20:34
by MoNoQLoREATOR
>>memcpyがあるので非PODで使うには危険
>>resizeで無駄にコンストラクタとデストラクタが走る
上記2点については理解できました。
1つ目について。要素として非PODのものを指定した場合に、resize()内で行われているmemcpyが危険だという意味だったのですね。
2つ目について。デストラクタは無駄には走っていないと思いますが、コンストラクタは無駄に走っていました。for文の部分ですね。配列をnewした際は、コンストラクタの引数が指定できないため、てっきりコンストラクタが呼び出されないのかと思っていました。ちゃっかりデフォルトコンストラクタが呼び出されているのですね。
ソースコードを修正してみました。ついでに、容量を実際に拡張(新しく領域を確保し、そちらにお引越しをすることによるもの)する際の効率が良くなるようにしてみました。
► スポイラーを表示
コード:
#define _CRT_SECURE_NO_DEPRECATE
#pragma once
namespace fw{
typedef unsigned int uint;
template<typename X>
class vector{
X * cont[2];
bool use;
uint Size;
uint space;
int Perm;
public:
vector(){
use = 0;
cont[use] = new X[256];
Size = 0;
space = 256;
Perm = false;
}
uint size() const { return Size; }
fw::vector<X> & resize(uint size){
if(size > Size){
if(size > space){
uint newSpace = size+256;
cont[!use] = new X[newSpace];
memcpy(cont[!use], cont[use], sizeof(X)*Size);
delete [] cont[use];
use = !use;
space = newSpace;
}
Size = size;
}
return *this;
}
fw::vector<X> & addsize(uint size){
resize(size+Size);
return *this;
}
fw::vector<X> & operator= (const std::vector<X> & sv){
resize(sv.size() );
Size = sv.size();
for(uint i=0;i<sv.size();i++) cont[use][i] = sv[i];
return *this;
}
X show(uint index) const {
uint needSize = index+1;
if(needSize > Size) delete [] cont[use];
return cont[use][index];
}
X & operator[] (uint index){
uint needSize = index+1;
if(needSize > Size){
if(Perm){
if(Perm==-1) resize(needSize);
else{
if(needSize<=(uint)Perm) resize(needSize);
else delete [] cont[use];
}
}
else delete [] cont[use];
}
return cont[use][index];
}
fw::vector<X> & pop(){
Size--;
return *this;
}
fw::vector<X> & add(){
addsize(1);
return *this;
}
fw::vector<X> & add(const X & input){
addsize(1);
cont[use][size()] = input;
return *this;
}
fw::vector<X> & add(const fw::vector<X> & input){
perm();
for(uint i=0;i<input.size();i++) cont[use][size()] = input.show(i);
lim();
return *this;
}
fw::vector<X> & operator++ (){
add();
return *this;
}
fw::vector<X> operator++ (int){
fw::vector<X> send = *this;
add();
return send;
}
fw::vector<X> & operator+= (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator+= (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> operator+ (const X & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> operator+ (const fw::vector<X> & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> & perm(){
Perm = -1;
return *this;
}
fw::vector<X> & perm(uint limit){
Perm = limit;
return *this;
}
fw::vector<X> & lim(){
Perm = 0;
return *this;
}
~vector(){
delete [] cont[use];
}
};
}
Re: C2662エラーについて
Posted: 2012年3月06日(火) 21:05
by MoNoQLoREATOR
memcpyについて。
vectorにおいての挙動を下記のソースコードを用いて実験してみました。
► スポイラーを表示
コード:
#include <vector>
using namespace std;
void main(){
vector<int> * v = new vector<int>[3];
for(int i=0;i<3;i++) for(int j=0;j<5;j++) v[i].push_back(j);
vector<int> * v2 = new vector<int>[3];
memcpy(v2,v,sizeof(vector<int>)*3);
for(int i=0;i<3;i++) for(size_t j=0;j<v[i].size();j++) printf("%d,", v[i][j]);
printf("\n");
for(int i=0;i<3;i++) for(size_t j=0;j<v2[i].size();j++) printf("%d,", v2[i][j]);
printf("\n");
}
結果は期待通りとなりました。
データを全てコピーしているので、当たり前と言えば当たり前かもしれません。
vectorの内部でどのような処理がなされているのかは私は知りませんので、期待通りの結果にならない可能性はあったわけですが・・・。
たとえ非PODなデータであっても、memcpyを用いることは危険ではないのではないかと思います。その辺りはどうなのでしょうか?
Re: C2662エラーについて
Posted: 2012年3月06日(火) 21:47
by softya(ソフト屋)
コピーコンストラクタではなくmemcpyを使うと内部情報(ポインタ等)まで複製してしまいます。
なので、こうするとvとv2が同じポインタを中途半端に共有していることが分かります。
コード:
#include <vector>
using namespace std;
void main(){
vector<int> * v = new vector<int>[3];
for(int i=0;i<3;i++) for(int j=0;j<5;j++) v[i].push_back(j);
vector<int> * v2 = new vector<int>[3];
memcpy(v2,v,sizeof(vector<int>)*3);
for(int i=0;i<3;i++) for(int j=0;j<5;j++) v[i].push_back(j);
for(int i=0;i<3;i++) for(size_t j=0;j<v[i].size();j++) printf("%d,", v[i][j]);
printf("\n");
for(int i=0;i<3;i++) for(size_t j=0;j<v2[i].size();j++) printf("%d,", v2[i][j]);
printf("\n");
}
Re: C2662エラーについて
Posted: 2012年3月06日(火) 23:16
by MoNoQLoREATOR
なるほど、たしかにそうですね。
今回のソースコードでは、コピー元の領域はすぐに解放していますし、コピー後は使用しないのでこの心配はありませんね。だからmemcpyを使うことによる危険性は今回の場合ないのではないかと思うのですがどうでしょうか?
Re: C2662エラーについて
Posted: 2012年3月06日(火) 23:46
by softya(ソフト屋)
MoNoQLoREATOR さんが書きました:なるほど、たしかにそうですね。
今回のソースコードでは、コピー元の領域はすぐに解放していますし、コピー後は使用しないのでこの心配はありませんね。だからmemcpyを使うことによる危険性は今回の場合ないのではないかと思うのですがどうでしょうか?
これでも大丈夫だと思いますか?
コード:
#include <vector>
using namespace std;
void main(){
vector<int> * v = new vector<int>[3];
for(int i=0;i<3;i++) for(int j=0;j<5;j++) v[i].push_back(j);
vector<int> * v2 = new vector<int>[3];
memcpy(v2,v,sizeof(vector<int>)*3);
for(int i=0;i<3;i++) for(size_t j=0;j<v[i].size();j++) printf("%d,", v[i][j]);
printf("\n");
for(int i=0;i<3;i++) for(size_t j=0;j<v2[i].size();j++) printf("%d,", v2[i][j]);
printf("\n");
delete[] v;
vector<float> * vf = new vector<float>[3];
for(int i=0;i<3;i++) for(int j=0;j<5;j++) vf[i].push_back(j+5);
for(int i=0;i<3;i++) for(size_t j=0;j<v2[i].size();j++) printf("%d,", v2[i][j]);
printf("\n");
}
Re: C2662エラーについて
Posted: 2012年3月07日(水) 04:31
by YuO
MoNoQLoREATOR さんが書きました:自覚があるのは、おかしな場所でdeleteしている点です。
「ユーザー視点において確保された容量」を超える要素にアクセスしてしまう事態が起こった場合は、範囲外アクセスエラーを出すべきだと思うわけですが、内部では「ユーザー視点において確保された容量」よりも多めに容量が確保されている状態であり、ほとんどの場合、自動で範囲外アクセスエラーが出ません。そこで、そのような事態が起こった場合は、確保した容量をdeleteしてしまうことによって強制的に範囲外アクセスエラーが出るようにしています。やはりtry~catch文を書いたほうがよいのでしょうか?
MoNoQLoREATOR さんが書きました:>>atでデータ構造が壊れる可能性がある
デリートしている部分のことでしょうか?容量が増える場合があるのは仕様です。
delete[]されたオブジェクトに対するアクセスは,「未定義の振る舞い」です。
おそらく,delete[]されたオブジェクトへアクセスしても,とりあえずは何事もないように動くでしょう。
で,次にnewされたあたりでオブジェクトが破壊されたように見えて,バグが見つかることになります。
範囲外アクセスを通知したいのであれば,std::vectorなどと同じく例外を明示的に発生させる必要があります。
MoNoQLoREATOR さんが書きました:>>memcpyがあるので非PODで使うには危険
PODという言葉が出てきたということは、ファイルに出力したりmemcpyを用いてデータをコピーしたりする場面でのお話ですよね?要素群のデータをファイルに出力したりmemcpyを用いてコピーしたりすることについてはなんら危険性はないはずです。このクラス自体に対してそのような処理をする場合は問題が出るかもしれませんが、通常そのようなことをすることはないでしょう。
コピーしたあと一切触らないのですか?
それであれば問題ないですが,そうではないですよね。
ポインタがあるようなクラスをmemcpyすると大抵の場合データ不整合が起きます。
# 二重deleteなんかもおきやすい。
クラスの作者はコピーコンストラクタや代入演算子のオーバーロードをしたり,場合によってはそれらを使えなくすることでデータの不整合を防いでいます。
しかし,memcpyはそれらをまったく無視するため,データがおかしくなり,思いもよらぬところで動作がおかしくなる原因となります。
確認ですが,以下のコードが未定義動作であることはちゃんと理解していますか。
コード:
#include <iostream>
#include <cstdlib>
#include <new>
class foo
{
private:
int * p;
friend std::ostream & operator << (std::ostream & stream, const foo & obj);
/*
C++11未対応の場合 (e.g. VC++) は,コメント直後の2行を
foo (const foo &);
foo & operator = (const foo &);
として,それぞれの本体を定義しないこと。
*/
foo (const foo &) = delete;
foo & operator = (const foo &) = delete;
public:
foo () : p(new int) { }
~foo () { delete p; }
};
inline std::ostream & operator << (std::ostream & stream, const foo & obj)
{
stream << &obj << " ( " << obj.p << " )";
return stream;
}
int main ()
{
foo * p1 = new foo();
foo * p2 = new foo();
std::memcpy(p2, p1, sizeof(foo));
std::cout << *p1 << std::endl;
std::cout << *p2 << std::endl;
delete p1;
delete p2;
return 0;
}
MoNoQLoREATOR さんが書きました:>>resizeで無駄にコンストラクタとデストラクタが走る
私にはコンストラクタやデストラクタが走っているようには見えません。
MoNoQLoREATOR さんが書きました:2つ目について。デストラクタは無駄には走っていないと思いますが、コンストラクタは無駄に走っていました。for文の部分ですね。配列をnewした際は、コンストラクタの引数が指定できないため、てっきりコンストラクタが呼び出されないのかと思っていました。ちゃっかりデフォルトコンストラクタが呼び出されているのですね。
使いもしないオブジェクトのコンストラクタが走り,それに伴い削除時にデストラクタが走るのが「無駄」です。
コード:
T * p = (T *)std::malloc(sizeof(T) * new_capacity);
for (std::size_t i = 0; i < new_capacity; ++i)
{
new (p + i) T(old_data[i]);;
}
のように,配置newを使って必要なオブジェクトだけをコピーコンストラクタで初期化し,さらに明示的にデストラクタを呼び出すことで,これらの無駄を省けます。
ちなみに,fw::vectorはVS2010sp1の環境下で257個目の要素を追加しようとした時にヒープが壊れていることが通知されました。
# PODじゃない (operator=がある) けど,intが二つとint *のみを持つだけでデストラクタのない (かつ不要な) クラスを渡した。
Re: C2662エラーについて
Posted: 2012年3月07日(水) 12:51
by MoNoQLoREATOR
>>softya(ソフト屋)さん
>>YuOさん
返信ありがとうございます。
なるほどそのようなケースも有りうるのですね。勉強になりました。
下記のようにソースコードを修正してみます。
・例外処理を加える
・memcpyを廃止。
Re: C2662エラーについて
Posted: 2012年3月07日(水) 14:14
by MoNoQLoREATOR
ソースコードは以下のようになりました。
► スポイラーを表示
コード:
#define _CRT_SECURE_NO_DEPRECATE
#pragma once
namespace fw{
typedef unsigned int uint;
template<typename X>
class vector{
X * cont[2];
bool use;
uint Size;
uint space;
int Perm;
public:
vector(){
use = 0;
cont[use] = (X*)malloc(sizeof(X)*256);
for(uint i=0;i<256;i++) cont[use][i] = X();
Size = 0;
space = 256;
Perm = false;
}
uint size() const { return Size; }
fw::vector<X> & setsize(uint size){
if(size > Size){
if(size > space){
uint newSpace = size+256;
cont[!use] = (X*)malloc(sizeof(X)*newSpace);
for(uint i=0;i<Size;i++) cont[!use][i] = cont[use][i];
for(uint i=Size;i<newSpace;i++) cont[!use][i] = X();
free(cont[use]);
use = !use;
space = newSpace;
}
}
Size = size;
return *this;
}
fw::vector<X> & reqsize(uint size){
if(Size < size) setsize(size);
return *this;
}
fw::vector<X> & addsize(uint size=1){
setsize(size+Size);
return *this;
}
fw::vector<X> & popsize(uint size=1){
int ressize = Size - size;
if(ressize < 0) ressize = 0;
setsize(ressize);
return *this;
}
fw::vector<X> & operator= (const std::vector<X> & sv){
setsize(sv.size() );
for(uint i=0;i<sv.size();i++) cont[use][i] = sv[i];
return *this;
}
X show(uint index) const {
uint needSize = index+1;
if(needSize > Size) MessageBox(NULL, "エラー", "範囲外アクセスが発生しました", MB_OK);
return cont[use][index];
}
X & operator[] (uint index){
uint needSize = index+1;
if(needSize > Size){
if(Perm > 0 || Perm==-1) setsize(needSize);
else MessageBox(NULL, "エラー", "範囲外アクセスが発生しました", MB_OK);
}
return cont[use][index];
}
fw::vector<X> & pop(uint size=1){
Size -= size;
return *this;
}
fw::vector<X> & add(){
addsize(1);
return *this;
}
fw::vector<X> & add(const X & input, uint size=1){
perm();
for(uint i=0;i<size;i++) cont[use][Size-1+i] = input;
lim();
return *this;
}
fw::vector<X> & add(const fw::vector<X> & input){
perm();
for(uint i=0;i<input.size();i++) cont[use][size()] = input.show(i);
lim();
return *this;
}
fw::vector<X> & operator++ (){
add();
return *this;
}
fw::vector<X> operator++ (int){
fw::vector<X> send = *this;
add();
return send;
}
fw::vector<X> & operator+= (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator+= (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> operator+ (const X & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> operator+ (const fw::vector<X> & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> & perm(){
Perm = -1;
return *this;
}
fw::vector<X> & perm(uint limit){
Perm = limit;
return *this;
}
fw::vector<X> & lim(){
Perm = 0;
return *this;
}
~vector(){
delete [] cont[use];
}
};
}
ちなみに仕様に問題があったので修正しました。
► スポイラーを表示
コード:
基本はstd::vectorと同じである。
ユーザーは、resize(),addsize(),add()などの関数によって容量を確保し、添字によって要素にアクセスするという理解で良い。
要素にアクセスする際、そのインデックス番号がユーザーの確保した容量を超えており、かつ自動拡張が許可されていない場合はアプリケーションエラー(範囲外アクセスエラー)が発生する。
尚、全ての要素が連続的かつインデックス番号順にメモリに格納されていることが保証されている。
つまり、インデックス番号0番目の要素が示すアドレスを先頭アドレスとし、通常の配列のように扱うことができる。
~メンバ関数一覧~
●オリジナル
コンストラクタ・・・引数はない
fw::vector<X> & operator= (const std::vector<X> & sv) ・・・ 本家を代入することもできる。
fw::vector<X> & perm(uint size) :
perm は permit の略で"許可する"の意。
現在の容量+指定した容量までの自動拡張が許可される。
自動拡張が許可されている場合、添字によってアクセスした際に、必要なら自動で新しい容量が確保される。
引数は省略することができ、その場合無制限に自動拡張が許可される。
ちなみにデフォルトでは許可されていない。
fw::vector<X> & lim() ・・・ lim は limit の略で"制限する"の意。自動拡張を許可しない設定にする。
fw::vector<X> & setsize(uint size) ・・・ 強制的に要素数をsize個にする。新たに確保された容量はデフォルトコンストラクタで初期化される。
fw::vector<X> & reqsize(uint size) ・・・ 最低でもsize個の容量が確保されている状態にする。容量が減ることはない。
fw::vector<X> & addsize(uint size=1) ・・・ 内部で setsize(size()+size) が行われる。
fw::vector<X> & popsize(uint size=1) ・・・ 内部で setsize(size()-size) が行われる。size()-size の結果が負になった場合は setsize(0) が行われる。
fw::vector<X> & pop(uint size=1) ・・・ popsize()に同じ。
fw::vector<X> & add() ・・・ 要素1つ分の容量が新しく確保される。領域は要素のデフォルトコンストラクタで初期化される。
fw::vector<X> & add(const X & input, uint size=1) ・・・ 本家push_back()に同じ。ただし size に指定した数だけ増やすこともできる。
fw::vector<X> & add(const fw::vector<X> & input) ・・・ 指定したvectorの内容が追加される。容量も必要な分確保される。
fw::vector<X> & operator++ () ・・・ add() に同じ。
fw::vector<X> operator++ (int) ・・・ add() に同じ。ただし、返値は add() を行う前のデータである。
fw::vector<X> & operator+= (const X & input) ・・・ add(const X & input) に同じ。
fw::vector<X> & operator+= (const fw::vector<X> & input) ・・・ add(const fw::vector<X> & input) に同じ。
fw::vector<X> & operator<< (const X & input) ・・・ add(const X & input) に同じ。
fw::vector<X> & operator<< (const fw::vector<X> & input) ・・・ add(const fw::vector<X> & input) に同じ。
fw::vector<X> operator+ (const X & target) ・・・ 自分のデータの後に target のデータが追加されたデータが返される。
fw::vector<X> operator+ (const fw::vector<X> & target) ・・・ 自分のデータの後に target のデータが追加されたデータが返される。
X show(uint index)const ・・・ 指定したインデックスの要素のコピーが返される。
●本家と同様
size()const
operator[]
例外処理を書いているときに疑問がわいてきたのですが、 abort()関数で強制終了させた場合にメモリ領域は解放されるのでしょうか?よくわからなかったので、とりあえず範囲外アクセスが発生した旨を伝えるメッセージボックスを表示させるだけにしておきました。
容量を増やす処理は、ご覧の通り配置newを使わずに書きましたがこれでは駄目なのでしょうか?
Re: C2662エラーについて
Posted: 2012年3月07日(水) 15:53
by YuO
MoNoQLoREATOR さんが書きました:例外処理を書いているときに疑問がわいてきたのですが、 abort()関数で強制終了させた場合にメモリ領域は解放されるのでしょうか?
標準規格の範囲外です。
現実的には,外部環境が勝手に回収すると思いますが。
MoNoQLoREATOR さんが書きました:よくわからなかったので、とりあえず範囲外アクセスが発生した旨を伝えるメッセージボックスを表示させるだけにしておきました。
とりあえずの対応であればよいですが,ユーザーへの通知は最終的なプロダクトを作成する側の役割です。
ライブラリが勝手にユーザーへ通知するのは,たいていの場合使いにくくなるだけです。
MoNoQLoREATOR さんが書きました:容量を増やす処理は、ご覧の通り配置newを使わずに書きましたがこれでは駄目なのでしょうか?
だめです。代入先にオブジェクトを構築していません。
コード:
struct bar {
bar ()
{
std::cout << "constructor : " << this << std::endl;
}
~bar ()
{
std::cout << "destructor : " << this << std::endl;
}
bar (const bar & other)
{
std::cout << "copy constructor : " << this << " from " << &other << std::endl;
}
bar & operator= (const bar & other)
{
std::cout << "operator = : " << this << " from " << &other << std::endl;
return *this;
}
};
というクラスをfw::vectorの要素にして,代入が構築済みのものに対してのみ行われることを確認してみてください。
Re: C2662エラーについて
Posted: 2012年3月07日(水) 19:58
by MoNoQLoREATOR
>>YuOさん
返信ありがとうございます。
コードを以下のようにしてみました。
► スポイラーを表示
コード:
#define _CRT_SECURE_NO_DEPRECATE
#pragma once
namespace fw{
typedef unsigned int uint;
template<typename X>
class vector{
X * cont[2];
bool use;
uint Size;
uint space;
int Perm;
public:
vector(){
use = 0;
cont[use] = (X*)malloc(sizeof(X)*256);
for(uint i=0;i<256;i++) new(&cont[use][i]) X();
Size = 0;
space = 256;
Perm = false;
}
uint size() const { return Size; }
fw::vector<X> & setsize(uint size){
if(size > Size){
if(size > space){
uint newSpace = size+256;
cont[!use] = (X*)malloc(sizeof(X)*newSpace);
for(uint i=0;i<Size;i++) new(&cont[!use][i]) X(cont[use][i]);
for(uint i=Size;i<newSpace;i++) new(&cont[!use][i]) X();
cont[use]->~X();
free(cont[use]);
use = !use;
space = newSpace;
}
}
Size = size;
return *this;
}
fw::vector<X> & reqsize(uint size){
if(Size < size) setsize(size);
return *this;
}
fw::vector<X> & addsize(uint size=1){
setsize(size+Size);
return *this;
}
fw::vector<X> & popsize(uint size=1){
int ressize = Size - size;
if(ressize < 0) ressize = 0;
setsize(ressize);
return *this;
}
fw::vector<X> & operator= (const std::vector<X> & sv){
setsize(sv.size() );
for(uint i=0;i<sv.size();i++) cont[use][i] = sv[i];
return *this;
}
X show(uint index) const {
uint needSize = index+1;
if(needSize > Size) MessageBox(NULL, "エラー", "範囲外アクセスが発生しました", MB_OK);
return cont[use][index];
}
X & operator[] (uint index){
uint needSize = index+1;
if(needSize > Size){
if(Perm > 0 || Perm==-1) setsize(needSize);
else MessageBox(NULL, "エラー", "範囲外アクセスが発生しました", MB_OK);
}
return cont[use][index];
}
fw::vector<X> & pop(uint size=1){
Size -= size;
return *this;
}
fw::vector<X> & add(){
addsize(1);
return *this;
}
fw::vector<X> & add(const X & input, uint size=1){
perm();
for(uint i=0;i<size;i++) cont[use][Size-1+i] = input;
lim();
return *this;
}
fw::vector<X> & add(const fw::vector<X> & input){
perm();
for(uint i=0;i<input.size();i++) cont[use][size()] = input.show(i);
lim();
return *this;
}
fw::vector<X> & operator++ (){
add();
return *this;
}
fw::vector<X> operator++ (int){
fw::vector<X> send = *this;
add();
return send;
}
fw::vector<X> & operator+= (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator+= (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const X & input){
add(input);
return *this;
}
fw::vector<X> & operator<< (const fw::vector<X> & input){
add(input);
return *this;
}
fw::vector<X> operator+ (const X & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> operator+ (const fw::vector<X> & target){
fw::vector<X> send = *this;
send.add(target);
return send;
}
fw::vector<X> & perm(){
Perm = -1;
return *this;
}
fw::vector<X> & perm(uint limit){
Perm = limit;
return *this;
}
fw::vector<X> & lim(){
Perm = 0;
return *this;
}
~vector(){
delete [] cont[use];
}
};
}
配置newの場合はdeleteする必要は無いという理解でよいのでしょうか?
例外処理についてなのですが。
>>ユーザーへの通知は最終的なプロダクトを作成する側の役割です。
>>ライブラリが勝手にユーザーへ通知するのは,たいていの場合使いにくくなるだけです。
では何も言わずに強制終了させた方が良いのでしょうか?
Re: C2662エラーについて
Posted: 2012年3月07日(水) 20:41
by bitter_fox
MoNoQLoREATOR さんが書きました:
例外処理についてなのですが。
>>ユーザーへの通知は最終的なプロダクトを作成する側の役割です。
>>ライブラリが勝手にユーザーへ通知するのは,たいていの場合使いにくくなるだけです。
では何も言わずに強制終了させた方が良いのでしょうか?
例外を投げればいいんじゃないですか?
Re: C2662エラーについて
Posted: 2012年3月07日(水) 22:18
by YuO
MoNoQLoREATOR さんが書きました:配置newの場合はdeleteする必要は無いという理解でよいのでしょうか?
- deleteしてはいけません
- デストラクタは明示的に呼び出さなければなりません
MoNoQLoREATOR さんが書きました:例外処理についてなのですが。
>>ユーザーへの通知は最終的なプロダクトを作成する側の役割です。
>>ライブラリが勝手にユーザーへ通知するのは,たいていの場合使いにくくなるだけです。
では何も言わずに強制終了させた方が良いのでしょうか?
今回の場合,必要なのは呼び出し元にエラーを通知することです。
方法は例外だったり,戻り値だったり,グローバル変数又はそれに類するものだったりしますが。
# 今回は例外がおそらく妥当。
強制終了などの重要な挙動は,本当にそれが妥当で,他に代替手段がないか考えた方が良いです。
Re: C2662エラーについて
Posted: 2012年3月08日(木) 09:38
by MoNoQLoREATOR
>>bitter_foxさん
>>YuOさん
返信ありがとうございます。
throw文を書けば、その後の処理はかってにやってくれるということでしょうか?
自分で定義しないといけないのかと思っていました。
例外が発生した場合の処理はユーザーに任せるという意味でしょうか。
本家vectorはどのような挙動でしたっけ?
範囲外アクセスが発生した旨が通知されてプログラムが終了したと思うのですが。
>>deleteしてはいけません
>>[* ]デストラクタは明示的に呼び出さなければなりません
fw::vector<X>::~X の事でしょうか。見落としていたデストラクタの修正を行いました。
↓デストラクタの部分だけ抜き出し
► スポイラーを表示
コード:
~vector(){
cont[use]->~X();
free(cont[use]);
}
Re: C2662エラーについて
Posted: 2012年3月08日(木) 11:41
by YuO
MoNoQLoREATOR さんが書きました:例外が発生した場合の処理はユーザーに任せるという意味でしょうか。
恐らく違うように考えていると思います。
ライブラリを使っている人,つまりはライブラリのユーザーに処理を任せます。
その結果,プロダクトのユーザーにMessageBoxで通知されるかもしれませんし,プログラムの内部で吸収されて処理されるかもしれません。
現在は「ライブラリの作成者」==「ライブラリのユーザー」なのだとは思いますが,この2つの立場は分けて考えた方がよいです。
MoNoQLoREATOR さんが書きました:本家vectorはどのような挙動でしたっけ?
範囲外アクセスが発生した旨が通知されてプログラムが終了したと思うのですが。
プログラムが終了する,というのはstd::vector::atの挙動の範囲ではありません。
atで範囲外アクセスが発生した場合,std::out_of_range_exceptionが発生します。
ライブラリのユーザーは,try - catchによって範囲外アクセスの発生を知ることが出来ます。
try - catchしないのはライブラリのユーザーの判断です。
MoNoQLoREATOR さんが書きました:>>deleteしてはいけません
>>[* ]デストラクタは明示的に呼び出さなければなりません
fw::vector<X>::~X の事でしょうか。
placement-newに関する一般論です。
Re: C2662エラーについて
Posted: 2012年3月08日(木) 14:50
by MoNoQLoREATOR
>>YuOさん
返信ありがとうございます。
通常は例外を投げるだけで、#defineにて特定の定数が定義されている場合のみメッセージボックスが表示される仕様にしてみました。
該当部分↓
► スポイラーを表示
コード:
X show(uint index) const {
uint needSize = index+1;
if(needSize > Size){
#ifdef _FW_VECTOR_POP_UP_
MessageBox(NULL, "エラー", "範囲外アクセスが発生しました", MB_OK);
#endif
throw std::out_of_range("範囲外アクセスエラー");
}
return cont[use][index];
}
X & operator[] (uint index){
uint needSize = index+1;
if(needSize > Size){
if(Perm > 0 || Perm==-1) setsize(needSize);
else{
if(needSize > Size){
#ifdef _FW_VECTOR_POP_UP_
MessageBox(NULL, "エラー", "範囲外アクセスが発生しました", MB_OK);
#endif
throw std::out_of_range("範囲外アクセスエラー");
}
}
}
return cont[use][index];
}
Re: C2662エラーについて
Posted: 2012年3月08日(木) 15:02
by softya(ソフト屋)
C++ではアンダースコアで始まる識別子はシステムの予約語です。
あとダブル・アンダースコアは識別子の何処に書いても予約語です。
「命名規則 (プログラミング) - Wikipedia」
http://ja.wikipedia.org/wiki/%E5%91%BD% ... A8_C.2B.2B
「take short 二重インクルード防止のシンボル」
http://pdragon.blog24.fc2.com/blog-entry-146.html
Re: C2662エラーについて
Posted: 2012年3月08日(木) 18:10
by MoNoQLoREATOR
>>softya(ソフト屋)さん
返信ありがとうございます。修正しました。
自分のソースコードを見直してみて、問題はもう無いかなと思いましたので『解決』といたします。
softya(ソフト屋)さん,YuOさん,bitter_foxさん 本当にありがとうございました。