【初心者歓迎】C/C++室 Ver.102【環境依存OK】
■ このスレッドは過去ログ倉庫に格納されています
エスケープシーケンスやWin32APIなどの環境依存なものもOK
そのような質問は必ず環境を書きましょう
半角空白やタブでのインデントはスレに貼ると無くなります
コードを貼れる所
http://codepad.org/
https://ideone.com/
前スレ
【初心者歓迎】C/C++室 Ver.101【環境依存OK】
https://mevius.5ch.net/test/read.cgi/tech/1500329247/ Visual Studioなどでデバッグするとヘッダーが見れるよ。vectorはテンプレートのクラスだから、
読み進めるにはテンプレートの知識が必要かもね。 >>664
ありがとうございました。
ディープコピーでしたら、使う上では、何も考えずに関数の引数として渡したり、
関数から返したりしてもかまわないですね。
>>665
ありがとうございました。 >>666
それはディープコピーのコストがかさむのでおすすめしない >>666
>何も考えず引数で渡しても良いですね
何をもってして「良い」と評価するのかはわかりませんが、
そういうのは拙い知識で独断せずそこら中にある
経験豊富な先人の書いたコードを読んで真似するのがいいですよ
引数は大抵 const 参照で渡しているでしょう class TestA
{
TestA() {}
~TestA() {}
};
class TestB
{
TestB() {}
~TestB() {}
TestA getA() { return TestA(); }
};
int main() { TestB b; TestA a = b.getA(); }
TestBクラスの getA() メソッドがこのテストコードで正しく動作しているのですが、TestAの
インスタンスは一体どこで作られてるんでしょうか?
TestA getA() { TestA a; return a; }
としないでいきなり reurn TestA() で良い理由が良く分かりませんでした。 >>669
どこのメモリにどう作られているのかという質問だと思うけど、
C や C++ では式の値として構造体やオブジェクトが認められていて、
コンパイラがどこぞにメモリを用意して上手いこと作ってくれることになっている。
式や式の一部の値としてだけ現れて変数に格納されないオブジェクトはテンポラリーオブジェクトとか言うので、
詳細はテンポラリーオブジェクトでぐぐって。
>>669
のmain はテンポラリーオブジェクトを使って b を用意せず TestB().getA(); ともかける
ところで今回の質問の、関数の戻り値に関するコンパイラの動作は
「戻り値最適化」なる最適化によって色々複雑なことになっているので
これは自分で戻り値最適化でググって調べて欲しい。 >>669
質問が文法的なことについてだとすると、式中の項ととして
TestA()
std::string("foo")
などのように型名にコンストラタの引数を加えて関数呼び出しのように書くと
テンポラリーオブジェクトを生成してそれを値とする頃ということになる。
例
size_t l = std::string("abc").length();
std::string 型のテンポラリーオブジェクト(中身は"abc") が作られ、
それのメンバ関数 length が呼ばれ、l はその値(3)で初期化される。 >>670-671
詳細をありがとうございました。
大変良く解りました。より深くは自分でググってみます。 for (int i = 0; i < n + 1; ++i) {;}
とやると、 i が n + 1 未満かどうかの判定をする際、毎度
n + 1 を計算してそれを i と比較するということになるので
しょうか?
もし、そうなら、
m = n + 1
for (int i = 0; i < m; ++i) {;}
としたほうがよいのでしょうか? >>674
ただの整数の足し算程度なら最適化されることが期待できるのであまり気にしなくていい。 https://ideone.com/TVzVzF
上記のコードで、read関数を持ったIReadableクラス(基底クラス)があって、そのクラスから派生したCFileReaderクラス、
直接は派生していないけどread関数を持ったCMemoryReaderクラスがあって、その両方を受け取れるvector<IReadable*>を作りたいのですがコンパイルが通りません。
どうしたらいいでしょうか? どなたか教えてください。。。 >>674
いまだに最適化されないコンパイラもあることはある
8bitマイコンとかの話
あと、nがvolatileだと当然毎回計算する
nがSFRだったり複数スレッド共有変数だったりするなら気を付けよう
普通は気にしなくて大丈夫
コンパイラの最適化の基本の基本なので
このループの比較がパフォーマンスに大きく影響するなら
高速化テクニックは色々とある >>677
なんとなく互換性のある read を持つものをエンベロープして IReadable になるエンベロープクラスを用いるのはどう?
ifstream も IReadable じゃないからエンベロープする。
https://ideone.com/27yhhu >>674
> for (int i = 0; i <= n; ++i) {;} for ループといえばつい手癖で
for (int i =0; i < count-1; ++i ) {...}
と書いてて、
後日に count を int から size_t にしたとき
countが0のケースではまったことがあるなあ
for (int i =0; i + 1 < count; ++i ) {...}
と書けば良いだけなんだけど >>681
それcountが1でも全くループしないというヘンテコ仕様だけど
そんなもの手癖で書くの? >>683
隣り合った2項、例えばv[i] と v[i+1] を用いた処理をするとか
後続のものがある項だけ処理するとか良くある int **a を利用して2次元の動的配列を作ります。
a を関数に渡して、計算に利用します。
2次元配列 a の要素は変更しないので、
int func(const int **a){ ... }
としました。
main 内には以下のように書きました。
ret = func(a);
すると、 a を int** から const int** に変換できませんというエラーが出てしまいます。
これはなぜでしょうか? >>675
>>678
>>680
ありがとうございました。 >>683
そんなにヘンテコでもないと思う
for (size_t count = 0 ; count < 10 ; count++){
for (size_t i = 0 ; i < count - 1 ; i++){
....
}
}
こんなのはありがちかと >>687
レアケースっていうか
nが小数の場合だな ふむ、なくはないか。
やはり条件の方に+1とか-1とか書くんじゃなくて初期値変える方が素直だろうな。
最適化の下手なコンパイラ対策も含めて。 >>689
だからそう言うのをレアケースって言ってるんだが...
よく書くと言うなら多分住む世界が違う >>685
具体的なコードは以下です:
int func(const int **a) {
return 0;
}
int main() {
int n = 10;
int **a = new int*[n];
for (int i = 0; i < n; ++i) {
a[i] = new int[n];
}
func(a);
for (int i = 0; i < n; ++i) {
delete[] a[i];
}
delete[] a;
} >>692
さくらの続編を放送してる今日この頃、レイアースの第三章もワンチャン有るかもな! >>687
そんな事言ったら
countが0でループだってレアケースだぞ >>693
const付きに変換出来るのは
ポインタの先がconstに変わる場合
int finc(int * const * a) なら大丈夫 ありがとうございます。
const についてよくわからないのですが、
func の中で、
a[i][j] = 1;
みたいなことをできないようにしたいのですが、どうすればいいでしょうか? >>679
ありがとうございます。その方法で取りあえずの目的は果たせそうなのですが、ほかの方法はないでしょうか?
というのも、他のクラスでラッピングしたりキャストしたりせずにifstreamを食わせたいのです。
たとえば、STLの各種関数はポインタでもvector等のコンテナでも大体OKですよね?
あれと同様に関数ポインタでもクラスのインスタンスへのポインタでもread()を持つものなら何でも突っ込めるようにしてforでぶん回したいのです。
標準のコンテナや自作クラス等の違いを吸収できるような構造にしたいのです。条件が後出しになってしまって申し訳ありません。 キャスト
が一番コストが少ないと思う
関数の中で
delete [] a[i];
a[i] = new int[10];
は出来ても良いの? >>699
ありがとうございます。
ちょっと回答が理解できないため、質問を代えさせてください
以下のプログラムの func はNGなのに func2 はOKなのはなぜでしょうか? int func(const int **a) {
return 0;
}
int func2(const int *b) {
return 0;
}
int main() {
int n = 10;
int **a = new int*[n];
for (int i = 0; i < n; ++i) {
a[i] = new int[n];
}
func(a);
int *b = new int[n];
func2(b);
for (int i = 0; i < n; ++i) {
delete[] a[i];
}
delete[] a;
delete[] b;
} >>701
それは c / c++ の欠点の1つで、
T * は const T * に文句も言わず変換してくれるが
T ** は const T ** に変換してくれないという問題
T は const T と定数性以外 compatible だよね、という判断を 1 段階しかしてくれない。
キャストが必要
const_cast<const int**>(a) など >>702
皆が言っているのは質問の内容とはちょっと違うけど、
func の中で a[0] = 0; などを禁止するために内側のポインタ自体も const にして
const int * const * にした方がいいよということ いやごめん
func(int * const * )にすればコンパイル通るのか…
T * const * ==> const T * const * という変換は暗黙でokなのか
自分がバカでした >>703
欠点なの?
T ** を const T ** に勝手に変換されると困るんだが。 T * を const T * には勝手に変換するけどな >>706
ちょっと興味があるので困るコードを教えて >>705
慌ててたのかこのレス書き間違ってた
func(const int * const * )にすればコンパイル通る、
T ** ==> const T * const * という変換は暗黙でok、
と書きたかった(全然違うな) >>709
int** ipp;
const int ci=0;
const int ** cipp;
cipp = ipp;
*cipp= &ci;
**ipp = 3; おっと ipp の初期化忘れた
int** ipp;
const int ci=0;
const int ** cipp;
int *ip;
ipp = &ip;
cipp = ipp;
*cipp= &ci;
**ipp = 3; ロベールの本に、
ios::out | ios::trunc
が意味がないと書いてあります。
既存のファイルを破棄してから書き込むということだとすると意味があるように思います。
ios::in | ios::out | ios::trunc とした場合のみ意味があると書いてあります。
どういう意味なのでしょうか? truncate は、ファイルサイズを切り詰め・縮小する。
これは、読み書き両用時に使える機能
ファイルを読み込んで、ファイルサイズを縮小して書き込む
ファイルの書き込みでは、使えない。
書き込みでは、ファイルサイズ0で開くから、前のファイルが消える >>712
なるほどね。
T ** => cont T ** は許すとcont T を操作できてしまうから禁止されていて、
T * => const T *
T ** => const T * const *
...(以下略)
にはその問題が無いから許可されているわけか file.read((char*)buf, sizeof buf);
は buf のサイズ分だけ読み込めということだと思います。
ファイルの最後の部分を読み込むとき、サイズが WIDTH 未満の場合にはどうなるのでしょうか?
fstream file;
file.open("hello.txt", ios::in | ios::binary);
do {
unsigned char buf[WIDTH];
file.read((char*)buf, sizeof buf);
for (int i = 0, size = file.gcount(); i < size; ++i) {
printf("%02X ", buf[i]);
}
cout << endl;
} while (!file.eof());
file.close(); 👀
Rock54: Caution(BBR-MD5:1341adc37120578f18dba9451e6c8c3b) >>717
ありがとうございました。
あともう一つ質問させてください:
ロベールの本なのですが、
int n = 0x41424344;
file.write((const char*)&n, sizeof n);
というコードがあるページにあります。
その少し後ろのページには、以下のコードがあります。
char buf[BUF_SIZE];
dst.write(buf, src.gcount());
ここで、なぜ
dst.write((const char*)buf, src.gcount());
としていないのでしょうか? >>718
char *はconst char *に互換だから。constは書き換えないという意味で、constなしはconstありに対して互換性がある。 すみません。もう一つ質問です。
またロベールの本なんですが、以下のコードが書いてあります。
ファイルから入力中にエラーが起きたときの対処法です。
if(src.fail()) {
ではなく、
if(src.fail() && ! src.eof()) {
と書いてあります。その理由として、
「読み込みの場合はファイルの終端に到達した際も fail メンバ関数が
真となるため、 eof メンバ関数が真になる場合は除外しておきます。」
と書いてあります。
そこで質問です。
ファイルの終端の直前の部分を読み込む最後の読み込みの際に何等かのエラーが起こった場合、
src.fail() == true かつ src.eof() == true となります。
そのため、
src.fail() && ! src.eof() == false となってしまいます。
この場合、読み込みエラーが起こったにもかかわらず、その対処ができないことになりはしないでしょうか?
それともロベールさんのコードはOKなコードなんでしょうか? fstream src;
…
char buf[BUF_SIZE];
src.read(buf, sizeof buf);
if(src.fail() && ! src.eof()) {
error = true;
break;
} >>720
ありがとうございます。
つまりどちらもエラーにはならないということですね。
ですが、記述が統一していない理由というのは何か考えられるでしょうか?
int n = 0x41424344;
file.write((const char*)&n, sizeof n);
に const がついているのは、 n は int 型だから write に渡すときには絶対に
キャストしなければならない。(char *) でもいいが、どうせなら const もつけて
しまおうということですかね?
一方、
char buf[BUF_SIZE];
dst.write(buf, src.gcount());
の方は、キャストの必要がないからわざわざ (const char*) とキャストすることも
ないかなという感じですかね? ロベールさんの本は色々細かいことが書いてあって有用なのですが、なぜこういうコードなんだろう?という
疑問を持って読んでいくと次から次へと疑問が生まれてきて読み進むのが大変です。 Cスタイルのキャストは強力すぎて、効果が分かりにくいので、モダンでは、そのキャストは、reinterpret_cast<const char *>(&n)って書いた方がいい。 winsowsにはC#コンパイラが標準付属してますが、C++コンパイラは標準付属してないんですか? つまり、その本は古い。温故知新とは言うが、Cスタイルのキャストの使用はモダンではない。 >>726
訂正。
× reinterpret_cast<const char *>(&n)
○ reinterpret_cast<char *>(&n)
そのCスタイルのキャストは
const_cast<const char *>(reinterpret_cast<char *>(&n))と同じ。
下手なキャストはバグのもと。キャストは最小限に。 コンパイラによっては、reinterpret_castとconst_castをCスタイルのキャストで一度にしようとすると、警告やエラーになるものもある。 file.write((const char*)&n, sizeof n);
ここでconstをわざわざ付けるのは、型で書き換えないことを確認するためだが、
このCスタイルのキャストは強力すぎて、行儀が悪い。reinterpret_castとconst_castに分けるべきだと考える。 元の趣旨がキャストについての質問だけどそれは置くとして、
そういうことするなら fstream じゃなくて basic_fstream<int> を使った方がよくなくなくない? そんなもんがいいと思ってる人に何言っても無駄だし
他人に強要しなけりゃそれ使ってもいいんじゃないの >>732
初心者が入門書で学習している段階で、途中の過程をすっ飛ばしてこっちの方がいいからこれ使えなんて進めるのはかえって理解を妨げるんでないの? 繰返すが趣旨と外れてることはわかってるので、元質問は忘れて。
こっちの手段もアリだよね? っていう別の質問。 >>736
競技プログラミングが流行る前には、宿題請負スレが隆盛を誇り、私もそれに便乗していろいろやっていましたが、
競プロをやりたいとは思わないなあ… ロベールの本に以下のコードがあります。
buf という配列ですが、 for 文の中で宣言されています。
PAGE_HEIGHT 回、配列 buf が作られるのでしょうか?
何か非効率的な気がします。
for (int h = 0; h < PAGE_HEIGHT; ++h) {
unsigned char buf[PAGE_WIDTH];
m_file.read((char*)buf, sizeof buf);
for (int w = 0, size = m_file.gcount(); w < size; ++w) {
printf("%02X ", buf[w]);
}
cout << endl;
} スタックポインタをずらす量が変わるだけ
これによる時間はゼロと思って良い >>739
そういうのを分かるようんなるにはどうすればいいのでしょうか?
C++の本だけ読んでいても分からないような気がします。 もしパフォーマンスが問題になるなら
printfをなんとかすべき >>740
コードを書いたときに具体的にどんな処理が行われるか
を地道に学んで行くしかない
アセンブラを見てもいいし本で学んでも良いし
時間を測っても良い
C言語の方が簡単なのでC言語にある機能から >>738
毎ループその buf が作られるかという質問について言えば
関数に入るときに確保された領域が毎ループ使い回されるだけ >>744
ありがとうございます。
newした場合にはもちろん毎回別の領域が確保されるわけですよね。
文法だけからでは分からないことだと思うので、そのような部分を解説した本が
あればよいのですが。。。 コンストラクターを起こすようなクラスならアレだけど
基本型はループの外においやらている可能性が高い
まぁ使ってるコンパイラの最適化次第というのはある
文法/規格だけで解決しない自由な部分は
実際に使ってるコンパイラの吐き出すコードをみるなり
実測して違いが出るか確認いてみたり……
初心の「それ無駄だろうという直感」は最適化で瑣末ごとになることが多い?かな? >>745
C++ スタック ヒープで検索すると解説しているページは山ほどヒットするけどいい本ってあるのかな。
CPUとメモリの動作とか簡単に学んでおくといい気がする。
スタックに収まらないような大きな領域をスタックに取ってはいけないとか
実践的に必要な知識でもあるんだよね。 >>748
個人的には、はじめて読むシリーズがコンパクトで良かった。
はじめて読む8086
はじめて読むPentium
はじめて読むMASM
はじめて読む486
486は結構ボリュームがある クラスの練習に文字列クラスっぽいものを作ったんですが
Mystr Mystr::operator=(Mystr &obj){
//左辺に右辺を代入
return *this
}
こうすると代入のたびに戻り値を返すためにコピーコンストラクタとデストラクタがわざわざ呼ばれてるみたいなんですが
関数の戻り値をvoidにする以外でなくす方法はありませんか?
一応コード全文 https://ideone.com/A1iQ3Y >>750
>Mystr Mystr::operator=(Mystr &obj){
一般的にはこうする
Mystr & Mystr::operator=(Mystr &obj){ Mystr & Mystr::operator=(const Mystr &obj); ありがとうございました
戻り値にも参照が使えることを知りませんでした >>753
おそらくわかっていると思うけど、ローカル変数や
テンポラリーオブジェクトの参照は返しちゃダメなので注意
ダメな例
T& f() { T a; .....; return a;}
string& g() { string a, b; .....; return a+b;} >>749
はじめて読む 486 は良書だけれでも、今、これを実際のマシンで試してみることはできなくなりましたね… アセンブラって「低水準言語」なはずだけども、今となっては機械語すらもかなり高水準だもんな……。
機械語の並びをコンパイルして最適化された μop にするみたいなことが CPU の中で起こってて、
機械の気持ちを理解するには機械語はまだまだ外側の方って感じ。
>>755
動かすための情報を集約しようとしてはしてるよ。
ある程度は動く。
https://github.com/tkmc/486 アドレスでアクセスできるメモリってものがあってデータやコードが書かれてるのかー
スタックなる仕組みでローカルな記憶域をほぼコストゼロで確保してんのかー
と言うことがわかれば十分な気がする >>758
>>749
これの上の3冊は薄くてすぐ読めるから、もやもやとした状態でいるよりはいい気がする。
ただ、486以外は古本でしか手に入らなそうだけど。 アセンブラの前に
まずは変数がどこにどのように確保されるかとか
どのように初期化されるかとか
そっちの方が先だろ
スタティック、スタック、ヒープ
をまず理解する
C++であればvirtual関数が呼ばれる仕組みとかも
知ってた方が良い
例外の仕組みは機種依存が大きいのでもうちょっと先で >>761
そう。
でもスタックを理解するにはメモリという概念モデルの理解が必要
だけど皆が勧めてるようなx86の解説書はやめたほうが良いと思う
セグメントレジスタとか原理を学ぶには邪魔なノイズが多過ぎる ■ このスレッドは過去ログ倉庫に格納されています