【初心者歓迎】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/ >>484
みなさん、ありがとうございました。
速度について気にしているのなら、ロベールにはそう書いてほしかったです。
例では、非常に小さな配列ですから速度について気にしていることは読み取りにくい
ように思います。
>>488
定数だから const にしたというのなら分かるのですが、なぜstaticをつけるのでしょうか? >>492
private なメンバはむしろ、クラスのメンバ関数を実装する .cpp ファイルに書いた方がいいように思います。 >>494
pimplイディオムで、クラス詳細を隠蔽できるよ。 >>493
あの部分では特別に速度を意識した記述ではなく普通に意識する程度の事だから一々説明されないということではないかな
速度や使用メモリを気にするのはCやC++プログラマの癖または習慣みたいなもので特にロベールみたいな昔の人は体に染み付いているだろうからね >>493
今回はたまたまmainであり、
たまたま小さなデータだったというだけで、
より汎用性の高いコードにするのはプログラミングの基本
staticを付けるメリットは
・データ構造の初期化が1回(ROMだと0回)で済む
・スタックを浪費しない
・関数を抜けてもデータが保持される
・番地が固定
デメリットは
・関数を通らなくても初期化される
・関数外でもメモリを使う 一番重要なのは>>491
PCプログラムしかやらない人は知らないだろうけど test2の形だと上手く動作しないのですが何故でしょうか
環境はvc6とstlport521です
void test1(const char* cstr, ...) {
char buf[1024];
va_list args;
va_start(args, cstr);
vsprintf(buf, cstr, args);
va_end(args);
printf(buf);
}
void test2(std::string str, ...) {
const char* cstr = str.c_str();
char buf[1024];
va_list args;
va_start(args, cstr);
vsprintf(buf, cstr, args);
va_end(args);
printf(buf);
}
void main() {
test1("hello1 %d\n", 123);
test2("hello2 %d\n", 123);
}
//結果
//hello1 123
//hello2 1819043176 引数を std::string &str, ... にすればなおる 引数を参照とポインタでも試してみましたが結果は変わらなかったです
void test3(std::string& str, ...) {
const char* cstr = str.c_str();
・・・
void test4(std::string* str, ...) {
const char* cstr = str->c_str();
・・・
void main() {
・・・
std::string str3("hello3 %d\n");
test3(str3, 123);
std::string str4("hello4 %d\n");
test4(&str4, 123);
}
//結果
//hello1 123
//hello2 1819043176
//hello3 1819043176
//hello4 1819043176 va_start(args, cstr);
なんでcstr? ああなんとなく意味が分かってきました
va_startはマクロであってcstrの部分は引数の変数名が指定されなければならないということですかね こういうコードにすると期待した動作をするようになりました
ありがとうございました
void test5(std::string str, ...) {
char buf[1024];
va_list args;
va_start(args, str);
vsprintf(buf, str.c_str(), args);
va_end(args);
printf(buf);
}
void main() {
・・・
test5("hello5 %d\n", 123);
}
//結果
//hello1 123
//hello2 1819043176
//hello3 1819043176
//hello4 1819043176
//hello5 123 ついでに言うと va_start の第二引数については仕様上結構な制限、
各 argument に課される制約と同じ制約がある
配列はダメ、参照はダメ、基本型が格上げされる型との互換性が必要、など
最後のはクラスオブジェクトや float はダメということ
仕様として結果が不定なだけで違反してもエラーにはならないし
意図した通りに動くことも多い
詳しくは default argument promotions va_start
で検索 va_listとC++は、相性が合わないんだよな。呼び出し側でc_str()するとか。 特に強い理由がなければ variadic template を使った方が安全だし簡単に書ける。
template<class... T>
void test6(std::string str, T... args) {
char buf[1024];
std::sprintf(buf, str.c_str(), args...);
std::printf(buf);
} えー、可変長引数の例にしただけなので、そんなどうでもいいとこを言われても困るよ。 そう言うところにしか突っ込めない雑魚の相手するなよ... >>511
なんでbufに一旦書いてるの?
直接printfじゃだめなん? 普通はね
クソコテがコードサイズ、互換性、実行速度など
全て犠牲にしてもテンプレートを使いたかったみたい va_start の第2引数の型の制限を知らなくてもor気にしなくても良いように
C++ らしく va_start 使わない例を出したんだろ…
と書いてはみたが、
そういう意図が通じるわけないか
通じる人は最初からわかってるもんな そもそも何でbufにって質問は
コメント元の>>507にすべきだろ こんな辺境ですらイキリ散らすなんて呆れる他ありませんわ 危険てんこ盛りなコードの危険性が低い部分をなおしてどうすんの?アホなの?
オナニーは隠れてやれ c++ スレで variadic template 紹介されてこの反応 >>526
危険性が高いかどうかは重要じゃなくて、話題のテーマに近いかどうかだろ。
本題に関係ないところなんてどうでもいいじゃないの。 >>501とvariadic templateは関係ないわけだが const double const ARRAY[] = { 3, -1 };
const double ARRAY[] = { 3, -1 };
の違いを教えてください。 ポインタ変数なら
・変数も指し先も書き換えられない
・変数は書き換え可能。 指し先は書き換えられない
のパターンだけど
対象が配列の場合、元々変数の書き換えができないから同じ意味じゃないかと >>531
> const double const ARRAY[] = { 3, -1 };
コンパイルエラーにならないんだっけ? >>532
ありがとうございました。
>>533
Visual Studioではコンパイルエラーになりません。 const double const a[];
は
const double a[];
と同じ意味
const const double const const a[];
なんて書いてもいいし
double const a[];
と書いてもいい
const typename と typename const と同じ
しかもconstはいくつ書いてもいい Win32のBOOL型を返す関数を複数回呼んで、
すべて成功したときのみTRUEを返したいのですが
(途中で失敗しても関数自体はすべて呼んでおく)、
BOOL Test()
{
BOOL bResult = TRUE;
bResult &= Api(...);
bResult &= Api(...);
bResult &= Api(...);
return bResult;
}
と書いてとりあえず動くのですが、よく考えたら&=はビット演算だし、
Win32のBOOL関数は成功時にTRUEを返すとは書いていないので、
例えば関数が成功時に2を返すパターンがあったら、
すべて成功しても戻り値はFALSEになるのではと思っています。
この場合、
bResult = Api(...) && bResult;
bResult = Api(...) && bResult;
bResult = Api(...) && bResult;
みたいな書き方をするしかないのでしょうか。 result &= !!api(...);
という風に!!を使うのも zero, non zero を false/true に正規化するイディオムだけど、
何にしても見た目は汚いと思う
文字数は増えるがif文を使って
if (!api()) result=false;
の方が可読性高いかもしれない >>539
成功した回数をカウントして呼び出す関数が3つなら3でTRUEにするかな
BOOL Test()
{
int count = 0;
if(Api() != FALSE) count++;
if(Api() != FALSE) count++;
if(Api() != FALSE) count++;
if(count == 3) return TRUE;
} 結果を std::vector にでもまとめておいて、 std::all_of で全てがFALSEでないことを確認するとか。 >>539
> bResult = Api(...) && bResult;
> bResult = Api(...) && bResult;
> bResult = Api(...) && bResult;
これでいいと思う
>>541
3個位ならいいけどたくさんになると数え間違いとかやらかしそう 普通に&で繋げてしまえば。
return Api()&Api()&Api()...; みなさんご意見ありがとうございます。
例を簡略化しすぎてしまいましたが、処理は状況によって分かれるため、
常に同じ関数が3回というわけではありませんでした。
失礼しました。
bResult = Api(...) && bResult;
の書き方自体は、そこまで汚いわけではないのですね。
同じような書き方をしているソースも、検索してみたら出てきました。
個人的には、
bResult &&= Api(...);
のような記述ができるとありがたかったです。 >>547
こういう手段もあるぞ。
bResult *= Api(...);
一度 0 になったら何をかけても 0 だ。 &&= と書けないことをこの質問で知ったわ。
>>548
「返り値が0でなければ成功」の関数で変テコな返り値が来ると
組み合わせでダメになるかも。
256 * 256 == 65536 で オーヴァーフローして0、みたいな感じ。 明確なメリットが無いのに、
無駄にトリッキーなコードを書くのはおすすめしない 数が少なく重要な箇所であれば、
デバッグ用コードを追加しやすい以下で良い
if (!Api(...)){
bResult = FALSE;
}
...
数が少なく重要じゃなければ >>547 で良い
数が多ければ色々と工夫しようか >>548
適当なニ数をかけたら 0 になってしまった、とかはあり得るのでしょうか?
…んー、ないな、何故ないのだろう? 基底クラスのメンバ関数に virtual をつけないことってあるんですか?
つけてもつけなくてもどちらでもいいという場合はあると思いますが、
つけちゃいけないという場合が考えにくいのですが、そういう場合は
あるのでしょうか? 逆に言うと、今のC++でvirtualをつけた場合の機能をvirtualをつけなかった場合の
デフォルトの機能にすればいいのにと思います。
どうでしょうか? >>553
仮想メソッドは、最適化がなければ関数ポインタのように確保されるので、不必要なものはメモリーの無駄になる。 >>555
virtualはつけなくてもかまわない場合には付けない方がいいということなんですね。
ありがとうございました。 >>557
せやな。 普通はないと思うが、無いと言い切ることもできない。
ここまでいろんな案が出てるけど、なんだかんだで >>539 が自分で結論出してるのがベストだと思う。
書き方の話なら、マクロでもクラスでも適当なものでラップすれば見栄えはどうとでも出来ることだし。 浮動小数点ならダーティー0とかあるけど
普通は意識しないでしょうねぇ 今回は「非ゼロ」が様々な数値である場合について考えているわけだから
0x80000 等掛けたらゼロになるケースを意識しないのはむしろ不自然 int64_t 使えばいいだろwww
ケチケチすんなってwwwww >>547
汚い書き方とまでは言わないけど、個人的にはあまりいいやり方だとも思わないかな。
俺もC, C++を覚えた当初はなるべく冗長な記述を省くことが正義であると考えていたけど、
そのうち高々数文字削ることなんかより、素直に可読性や保守性が高い記述をする方が美しいと感じるようになったよ。 !か!!使えば1か0にしかならんからそれかけるとか それをシフトと組み合わせると、エラー箇所までわかって便利だね >>556
補足すると、仮想関数は呼び出しのコストも同じ理由で非仮想のメンバ関数より高い(わずかだけど
あと、仮想関数が一つでもあるクラスは、そのクラスのオブジェクトの先頭に仮想関数テーブルへのポインタが入る
つまり純粋にメンバ変数のデータ通りのメモリイメージになるクラスが作れなくなる
virtualかどうかを選ぶ余地があるってことは選ぶ必要があるということだよ >>565
ネタだろうけど、ほんとにそれが必要としてもよほどメモリーに困ってるのでないなら>>542とかでいいだろ a=1に対して、
cout << a << a++なら21
cout << a++ << aなら12
cout << a++ << a++なら21
と表示されました。
coutはどんな順番で評価されているのでしょうか? >>571
確かに以下は
https://ideone.com/sBACG3
21
12
21
と表示される
でも、手元のVC 2017 / Windows 10 では
11
12
11
と表示された
g++ x86_64-posix-seh 7.1.0 / Ubuntu 16.04.3 LTS on Windows 10 では
11
12
12
と表示された >>572
http://codepad.org/KVv4KD8O
ではコンパイルエラーになった。
cc1plus: warnings being treated as errors
In function 'void test1()':
Line 6: warning: operation on 'a' may be undefined
In function 'void test2()':
Line 12: warning: operation on 'a' may be undefined
In function 'void test3()':
Line 18: warning: operation on 'a' may be undefined
http://techtipshoge.blogspot.jp/2012/01/c.html
http://www.kouno.jp/home/c_faq/c3.html
http://www.st.rim.or.jp/~phinloda/cqa/cqa7.html
こんな書き方をするなってことだね >>571
いくつかの例外を除いて式中の各項が評価される順序は決まっていないので、
評価される順を知りたいのなら実際にコンパイルされた結果のコードや挙動を見て調べるしかない。 未定義動作になるから、今回のコンパイル時と次回のコンパイル時とで
同じ順序で評価されない可能性もある、じゃないかな。
実際のところ、コンパイルごとに評価順が変化するとも思えないけど。
いずれにせよ「未定義動作は避けろ」が間違いない方針だわね。 >>573
おお、「未定義動作となること」を検出してコンパイルエラーにする環境もあるんだな class Base {
public:
void A();
protected
virtual void B();
}
void Base::A() {
B();
}
void Base::B() {
cout << "Base" << endl;
}
class Derived : public Base {
protected
virtual void B();
}
void Derived::B() {
cout << "Derived" << endl;
}
Base b;
b.A(); ⇒ 「Base」が表示される。
Derived d;
d.A(); ⇒ 「Derived」が表示される。
b = d;
b.A() ⇒ 「Base」が表示される。 b = d;
b.A() ⇒ 「Base」が表示される。
↑で、なぜ、「Derived」が表示されないのでしょうか?
ロベールの本に、
「仮想関数はどんな状況でもそのオブジェクトの本来の型のものが呼ばれる」
と書いてあります。 void Base::A() {
B();
}
void Base::B() {
cout << "Base" << endl;
}
A() の中で、B() を呼んでいるから
A(), B() は異なる関数なのに、呼ぶなんてあり得ないだろ。
こんなコーディングはしない >>578-579
よく分かりません。
ポリモーフィズムというのがありますが、その考え方だと
b = d;
b.A() ⇒ 「Derived」が表示される。
のではないかと思ってしまいます。 Base::B() の virtual を削除する:
class Base {
public:
void A();
protected
void B();
};
すると、
Base b;
b.A(); ⇒ 「Base」が表示される。
Derived d;
d.A(); ⇒ 「Base」が表示される。 ポインタじゃなくて実体をBase bに代入(コピー)したらDerivedじゃなくなるのは当然。「スライシング」でググれ。 まずこの本で、オブジェクト指向を学ぶ。
スッキリわかる Java入門 第2版、2014
最難関のC++ で、オブジェクト指向を学ぶなんて、夢のまた夢w
軽く、数年を無駄にするだけ
C++ なんて、偏差値70以上しか無理やのに >>577
> b = d;
> b.A() ⇒ 「Base」が表示される。
Base bp;
bp = &d;
b->A();
こうやね >>582
間違えた
Base *bp;
bp = &d;
b->A(); >>583-585
ありがとうございました。
C++が難しいというのは、設計が悪いからですか?
C++と同等の機能を持った言語で、C++よりも分かりやすい言語を新たに
設計することは無理ですか? >>587-588
ありがとうございました。
試してみようと思います。 理由があってC++を習得するのが難しいのならOKですが、設計が悪いから
難しいということになると、利用者にとっては迷惑な話ですね。 C++ は、何でも出来るようにしているから、ルール数が100以上ある
さらに、ルールAでは、B, C は除くとか、
1つのルールが、他のルールとからむから、
非常に難しいし、組み合わせ爆発が起こる
膨大な時間を無駄にしても、さほど理解できず、身につかない。
組み込み機器も想定しているから、
どうしてこういうルールが必要なのか、初心者には理解できない。
Rust ですら、初心者には無理だろ
だから、ドワンゴ江添の本を持って、数年山ごもりしろって言われる。
ルールの多さで、廃人になってしまう
初心者には、絶対に無理。
最初から、エベレストを登るようなもの。
まず「スッキリわかる Java入門」とかの、低い山で修行を積むべし
このレベルでは言語どうこうじゃない。
小中高大学まで行くような、研修制度・道筋が大事。
徐々に基礎体力を付けていかないと、何もできない ■ このスレッドは過去ログ倉庫に格納されています