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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

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

Q&A

解決済

2回答

683閲覧

C++分からない箇所があります。

strike1217

総合スコア651

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

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

0グッド

0クリップ

投稿2018/08/15 12:48

編集2018/08/15 13:24

1つ目は new に関してです。

newの実装
デフォルトのoperator newの実装例

void * operator new ( std::size_t size ) throw( std::bad_alloc ) { if ( size == 0 ) size = 1 ; // Executes a loop: Within the loop, while( true ) { // the function first attempts to allocate the requested storage. void * ptr = std::malloc( size ) ; // if the attempt is successful if ( ptr != nullptr ) {// Returns a pointer to the allocated storage return ptr ; } // Otherwise, std::new_handler handler = std::set_new_handler( nullptr ) ; std::set_new_handler( handler ) ; // if the argument in the most recent call to set_new_handler() was a null pointer, if ( handler == nullptr ) {// throws bad_alloc. throw std::bad_alloc() ; } // Otherwise, the function calls the current new_handler function. handler() ; // If the called function returns, the loop repeats. } }

また、こちらだと少し実装が異なるようです。
libc++の方ですね。
LLVM Download Page
ダウンロードしてビルドしてみました。

// Implement all new and delete operators as weak definitions // in this shared library, so that they can be overridden by programs // that define non-weak copies of the functions. _LIBCPP_WEAK void * operator new(std::size_t size) _THROW_BAD_ALLOC { if (size == 0) size = 1; void* p; while ((p = ::malloc(size)) == 0) { // If malloc fails and there is a new_handler, // call it to try free up memory. std::new_handler nh = std::get_new_handler(); if (nh) nh(); else #ifndef _LIBCPP_NO_EXCEPTIONS throw std::bad_alloc(); #else break; #endif } return p; }

んーーー。
newってmalloc使ってたんだ!!
「they can be overridden by programs」これはなんのことを言っているのかサッパリですが・・・
newの実装自体が、libc++とlibstdc++で異なっている可能性があります。
libstdc++をどこで手に入れるのかわからなかったので調べていないのですが・・・

ここで疑問3つ出てきます。
1,void をreturn している。*
2,malloc使っているのに、どうやってコンストラクタ呼び出しているのか??
3,パラメータがstd::size_tになっている

1番目と3番めについては、
int * ptr = new int
という風に使いますが・・・
*int * ptr = static_cast<int >(new sizeof(int))
上記の実装では、このように書かなくてはならないはずです。
なぜこうは書かかなくてもエラーやwarningにならないのでしょうか??

2番目については、自分の持っている本にこうあります。

operator new や operator delete について、よく誤解があるが、コンストラクタやデストラクタの呼び出しの責任は持たない。

え??そうなの?
では、なんで、mallocを使った時はコンストラクタを呼び出さないのに、newの時は呼び出すのでしょうか??

2つ目は、演算子についてです。

1 #include<iostream> 2 3 int max(int a, int b){ 4 return a < b ? b : a; 5 } 6 7 int main(){ 8 int max(int, int = 3 + !(std::cout << ",")); 9 10 for(int i = 0 ; i < 10; i++) 11 std::cout << max(i); 12 std::cout << std::endl; 13 return 0; 14 } 15

,3,3,3,3,4,5,6,7,8,9
と出力されます。

おお!すごい!デフォルトパラメータにこんな事ができるんだぁ!

ここで疑問なのが、+!の演算子です。
この組み合わせは初めて見ました。

色々試してみました。

int max(int, int = 3 + (std::cout << ",")); //error int max(int, int = 3 - !(std::cout << ",")); //ok int max(int, int = 3 & !(std::cout << ",")); // 結果が変わる int max(int, int = 3 ~ !(std::cout << ",")); //error

できる組み合わせとできない組み合わせがあります。
!演算子がない、+演算子だけの場合はうまくいかないのが引っかかります。
+!の組み合わせにどのような意味があるのでしょうか??

g++ Linux 64bitです。
分かる方いたらお願いします。

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

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

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

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

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

guest

回答2

0

ベストアンサー

2つの実装はよく読んだら_LIBCPP_NO_EXCEPTIONSで例外を投げるか投げないか以外は一緒ですよ。

new式とoperator newは別物です。
new式はoperator newを呼び出して領域を確保した後、コンストラクタを呼び出して指定した型のポインタとして返します。
operator newの役割は指定サイズの領域を確保するだけなので型は不明なためvoid* (size_t)で、コンストラクタを呼び出しようがありません。(特殊な例はあるかもしれませんが)
new式の形は文脈からコンパイラが決定するので上書きとかはないはず。

「they can be overridden by programs」これはなんのことを言っているのかサッパリですが・・・

これはライブラリの事情によるものです。
libc++は通常まとまったコンパイル済みライブラリとして存在するので、プログラム側がメモリの確保は自前のものにしたい(libc++のoperator newを完全に置き換えたい)場合に、グローバルなoperator newを作成してもlibc++のものがすでにあるのでリンク時に邪魔をしてしまいます。
そこで、_LIBCPP_WEAKを付けておくことでユーザー側(libc++使用側)に同じシンボルがあればそちらが利用されるようになります。
ですので、プログラムによってオーバーライド可能、ということです。
gccの拡張みたいですね。

+!の組み合わせにどのような意味があるのでしょうか??

もう書かれてますが、 (std::cout << ",") の戻り値は std::cout ですから、
!std::cout は std::cout.fail() となり、通常 false です。
C++ではbool型は暗黙的に整数に変換可能で false は 0 となるため、よっぽどのことがない限り結果は 3 + 0 となります。

投稿2018/08/15 15:14

編集2018/08/15 15:16
toki_td

総合スコア2850

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

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

strike1217

2018/08/15 15:23

> (std::cout << ",") の戻り値は std::cout ですから、!std::cout は std::cout.fail() となり ああ!わかりやすいです。 > 2つの実装はよく読んだら_LIBCPP_NO_EXCEPTIONSで例外を投げるか投げないか以外は一緒ですよ。 はい。ただ、std::mallocや::mallocなどの細かい部分に違いがあるので、この違いはなんだろ??と思ったのです。 libstdc++ や libc++などのライブラリ側で実装方法が少しだけ異なるのかな?と疑問におもいまして・・・
strike1217

2018/08/15 15:25

> new式はoperator newを呼び出して領域を確保した ん?ということは、 int *ptr = new int; これは、new式であり。 new式 -> operator newの順に呼び出しているということですか!!
strike1217

2018/08/15 15:31

new式って、ライブラリの方で定義されているんですか? それともコレってコンパイラが提供しているんでしょうか?
strike1217

2018/08/15 15:41

ん?なんか変ですね。 ちょっと待ってください。
toki_td

2018/08/15 15:42

> この違いはなんだろ??と メモリを確保することしか決まっていないので単純に実装者の違い程度だと思います。 > new式 -> operator newの順に呼び出しているということですか!! 順にというか、new式の中で行う処理のうちメモリを確保する部分がoperator newとして分離されていて、new式の型や引数で呼び出し先が変わったり置き換えたりできる、という感じでしょうか。 ざ~~っくり、こんな感じのものが作られると思えばいいかと(疑似コード) template<typeneme T> T* new () { auto p = (T*)operator new(sizeof(T)); p->T(); return p; } > ライブラリの方で定義されているんですか? new式の処理内容は文脈で一意に決まるのでコンパイラが自動生成するんじゃないかと(自信薄)
strike1217

2018/08/15 15:44

おお! なるほど! operator newのラッパー関数のようなものが存在しているんですね。 > コンパイラが自動生成するんじゃないかと fmfm!! なるほど! わかりました。 通りで、GDBやvscodeでやってもnew定義にジャンプできないわけですね。
strike1217

2018/08/15 15:45

> new式とoperator newは別物です。 ここが重要なのですね!
toki_td

2018/08/15 15:51

VCで逆アセンブラを見た感じではコンパイラが生成するみたいです。 コンストラクタを呼ぶ前にnullチェックが入る以外は↑と同じのが生成されてました。
strike1217

2018/08/15 15:54

あ!逆アセンブルという手段が残っていましたね! くっそーーー!!悔しい。 気づきませんでした。 ありがとうございます。 また何かあったらよろしくお願いします。
strike1217

2018/08/16 04:25

あ、すいません。1つだけ・・・ 他の演算子の場合でもコンパイラが勝手にラッパー関数みたいなものを生成する場合ってあるんでしょうか? operator + operator & などなど・・・ newだけが特別なのでしょうか?
guest

0

「they can be overridden by programs」これはなんのことを言っているのかサッパリですが・・・

new演算子はユーザーが定義することができます。完全にバッドプラクティス感ありますが。

ここで疑問3つ出てきます。

ちょっと規格書を読んでいないのであれですが、多分その実装を呼ぶ何かが我々が呼び出しているものかと。

追記

通常我々が呼び出しているのは operator new ではなくて new 式だからですね…
もちろん operator new を直接呼び出すこともできますが… https://t.co/MexiQy4XhG
— 置き引きにあったマヌケな鳥頭 (@kariya_mitsuru) 2018年8月15日

そうだった・・・

ここで疑問なのが、+!の演算子です。

そんな演算子はありません。普通に二項演算子+と単項演算子!です。

ただし単項演算子!
https://cpprefjp.github.io/reference/ios/basic_ios/op_not.html
こいつです。

つまり単項演算子!の戻り値の方がbool型で、これが二項演算子+を評価するときに、Integral Conversionによって1または0に変換されます。

ref: c++ - bool to int conversion - Stack Overflow

投稿2018/08/15 13:45

編集2018/08/15 15:07
yumetodo

総合スコア5850

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

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

strike1217

2018/08/15 13:51

> そんな演算子はありません。 ああ、説明が下手ですいません。その通りですね。 !演算子はてっきり否定演算子の方かと思っていました。 つまり、!(std::cout << "," ) は、0になるんですね。
yumetodo

2018/08/15 13:53

>!演算子はてっきり否定演算子の方かと思っていました。 そうですよ、それがoverloadされています。 > つまり、!(std::cout << "," ) は、0になるんですね。 とは限りません。cpprefjpをよく読んでください。
strike1217

2018/08/15 13:57

ああ、そうですね。 そうとは限りません。
strike1217

2018/08/15 14:12

std::cout などの標準入出力の継承関係を把握ていないので、わからないのですが・・・ std::basic_iosってstd::cout と関連ありましたっけ?? この場合、!演算子関数は、パラメータとして渡されているstd::coutとの関連からADLによって名前解決されている・・・と予測できますが・・・
yumetodo

2018/08/15 14:36

std::coutはstd::basic_ostream<char>型です。 https://cpprefjp.github.io/reference/iostream/cout.html std::basic_ostreamはstd::basic_iosを継承します。 https://cpprefjp.github.io/reference/ostream/basic_ostream.html 今回無関係ですが、std::basic_iosはstd::ios_baseを継承します。 https://cpprefjp.github.io/reference/ios/basic_ios.html 単項演算子`!`はstd::basic_iosのメンバ関数です。これまたcpprefjpをよく読めば書いてあります。
strike1217

2018/08/15 14:53

> std::basic_iosのメンバ関数です そうですね。これは大丈夫です。
strike1217

2018/08/15 14:59

ostream とbasic_ostreamって同じ物ですか? エイリアスかな?
strike1217

2018/08/15 15:03

https://msdn.microsoft.com/ja-jp/library/h07e2k12.aspx char に特殊化した basic_ostream がostreamなんですね。 なるほど! 「!演算子関数は、パラメータとして渡されているstd::coutとの関連からADLによって名前解決されている」で正しいようですね。
yumetodo

2018/08/15 15:09

> そうですね。これは大丈夫です。 フリー関数ではないのでADLじゃないという意味合いだったのですが
strike1217

2018/08/15 15:11

え?そうなんですか? あ!クラスだとADLが発動しないのか!! 失礼しました。
yumetodo

2018/08/15 15:12

> あ!クラスだとADLが発動しないのか!! そういう話じゃないなぁ・・・
strike1217

2018/08/15 15:15

え?違いましたっけ?? std::basic_ios::operator! って第一パラメータが暗黙の内に付きますよね。 あ! パラメータが、std::coutだから、オーバーロードの最適関数に選ばれるんですね。 ADLは無関係ですね。
strike1217

2018/08/15 15:17

継承関係は、 ios_base -> basic_ios -> basic_ostream となっているんですね。 basic_ostreamのchar特殊化でostreamになるということですね。
strike1217

2018/08/15 15:55

無事に解決しました。 ありがとうございました。
yumetodo

2018/08/15 17:04

うーん、パラメータがstd::coutってのも間違いじゃないけど語弊があって、あくまでopreator <<がstd::basic_ostream<char>&を返していて、それに対してoprator!があるので・・・ 間違ってないけどうーん・・・
yumetodo

2018/08/15 17:05

というか単項演算子!に引数ないですよね。thisはあるけど。argumentがないんだからADLも関係ない
strike1217

2018/08/16 03:44

> thisはあるけど そうですね。 それが第一パラメータ。と理解していますが・・・ ん・・・「std::coutをパラメータとしている」← 言葉が不適切ということですかね。 「std::basic_ostream<char>&をパラメータとしている」と言ったほうが正確ということでしょうかね。 そもそもthisをパラメータと呼ぶのが間違っているんですかね? ああ・・・言葉って難しいですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問