C++ 言語講習会第 0 回資料

書いた人:@sunaemon0(回路屋 2 年)
1 目標
• linukairo のソースコードを読むことが出来るようになる
• 効率的で安全なコードを書くための基礎知識を身につける
– 例外安全性を保つ
– 資源管理をおこなう
– 未定義動作を避ける
2 C++ ことはじめ
C++ は、C++98, C++03, C++11 と進化してきました。
C も K&R, C89, C99, C11 と進化してきました。
初期においては C++ は C の上位互換だったのですが、今では
そうではありません。
ただ、libkairo で使われている構文なら、ほとんどが C++ で
もそのまま動きます。
今回の講習会では C89 の知識を前提とした上で、C++03 ...
3 コンパイルの方法について
この pdf ファイルに同梱されているソースコードは以下のよ
うにコンパイルできます。
1

# g++ --std=c++11 example0 .cpp -W -Wall
4 C89 と C++03 の違い 1
• 一行コメントが追加された。 //コレです
• 関数内のブロックで先頭以外でも宣言を行えるように
なった。
• stdint.h がある。
• bool 型がある。
以上は C99 に入っているので使っ...
list 1 example0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# include <stdio .h>
int main(int , char **) // you can omi...
5 C++03 と C89 との違い 2
• 構造体を使いやすくするのに、C では typede をよく使い
ましたが、typedef なしで同じことができるようになりま
した。
• 引数を取らない関数について、int f(void) でなく、...
6 iostream
詳しいことは次回やりますが、C++ では printf/scanf の代わ
りに次のように書きます。
list 2 iostream0.cpp
1
2
3
4
5
6
7
8
9

# include <string >
...
以降、とりあえず::について解説します。
7 名前空間
C++ ではライブラリの間の名前の衝突を防止するため、
namespace というキーワードを使って名前空間を分離する
ことができるようになっています。それぞれの名前空間にア
クセスするには::演算子を使います。
list 3 namespace.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# include <iostream >
namespace foo
{
int f() { r...
大域スコープにはこのようにアクセスします。
list 4 namespace1.cpp
1
2
3
4
5
6
7
8
9
10
11

# include <iostream >
int f() { return 1;}
int main(i...
8 using
いちいち名前空間を指定するのが面倒な場合、using ディレ
クティブを使って名前空間を指定しなかった時に検索する名
前空間を登録することができます。
この際、関数名が衝突して曖昧さが発生した場合は、その関
数の使用時にコンパイ...
list 5 using0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# include <iostream >
namespace foo
{
int f() { retu...
9 Koenig Lookup(飛ばして良い)
このコードはコンパイルエラーが出ます。
list 6 koenig0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# include <iostream >
struct test {int a;};...
これだとコンパイルできます。
list 7 koenig1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# include <iostream >
namespace foo
{
struct tes...
どうして2つ目の例だとうまく行くかというと、C++ のコン
パイラはスコープ指定子がない関数の呼び出しが有った場合
に、大域スコープと無名スコープ、using ディレクティブで
導入されたスコープ、using で導入されたシンボル以外にも、
引...
10 標準ライブラリについて
c++03 からは c および c++ のライブラリを読み込むときに
1
2

# include <stdio .h>
# include <iostream .h>

でなく、
1
2

# include <...
11 関数のオーバーロード
c++ では引数の型が違う同名の関数を定義できます。
list 8 overload0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# include <stdio ...
型がぴったり合う関数定義があれば、それが呼ばれます。型
変換を行うことで呼び出せる関数があれば、それが呼ばれ
ます。
12 extern “C”
gcc でコンパイルすると、.o ファイルのシンボル名と、C で
書いた関数名が一致します。nm a.out として調べてみると
わかり安いです。やったことがある人なら解ると思います
が、違う翻訳単位で同名の関数を定...
C++ では同名でも型の違う関数が定義できます。これでな
んで ld に怒られないかというと単純にシンボル名に型の情報
を含めているためです。
例えば、
1
2
3
4

can_packet make_can_motor_packet (ui...
しかし、c で書かれたライブラリと c++ で書いたコードをリ
ンクするときにこの機能が悪さをします。
1

int getchar ();

みたいにしても変な名前のシンボル Z7getcharv を探しに
行ってしまってリンクがうまく出来ま...
13 スタックとヒープ
ローカル変数や呼び出し番地などが格納される領域をスタッ
ク領域と言います。

C で言えば、malloc で取って来れる領域のことをヒープ領域
と言います。
static 変数が配置される領域を静的領域といいます。
14 new と delete
c++ ではヒープ領域に変数を確保するのに new を使います。
開放するには、delete を使います。
list 9 new0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13

# inc...
配列を確保する場合は new[] を使います。また開放するには
delete[] を使います。
list 10 new1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13

# include <cstdio >
int ma...
new/delete は malloc/free に比べてキャストが必要がないと
いう点で型安全になっています。
開放するときに使う演算子が違うことに注意して下さい!
15 左辺値参照とポインタ
C++ では実はポインタはあまり使いません。
C でポインタを使う場面は多分この4つです。
1.
2.
3.
4.

参照渡しをする。
malloc を使ってヒープ領域に動的に領域を確保する。
文字列を扱う。(2. ...
今回やる左辺値参照というのはざっくり言うと参照渡しをす
るためのものです。
次回以降やりますが、文字列は std::string を使い、ヒープ領
域に配列を確保するには普通 std::vector を使います。

C++ でポインタが必要にな...
実引数を関数に渡すときに、値をコピーして渡すのを値渡し
と言います。

• 呼び出された側は、実引数を変更できません。
• 大きなデータを値渡しすると、大きなコストがかかり
ます。
実引数を関数に渡すときに、値をコピーせずそのまま渡すの
を参照...
ポインタで参照渡しを行うと以下の問題があります。

• 値渡しの時と記法が違う (同時に利点でもあります。)
• NULL を渡せてしまう
これらを解消したのが左辺値参照です。
左辺値参照は基本的にはポインタです。
宣言は
1
2

int a = 0; // local variable
int &r = a; // reference of a

このように行います。
左辺値参照はこのように、lvalue(ローカル...
で、rvalue(リテラルなどの名前のついていない一時的な値)
で初期化できます。
これはポインタとは全く違った振る舞いです。
参照渡ししてみましょう。
list 11 reference0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# include <cstdio >
void f(int &i)
{
i=3;
}
int main...
とは書きません。参照はその元の変数と同じように振る舞い
ます。
そのため、ポインタと違って free() や delete はできません。
16 オブジェクト指向
オブジェクト志向というのは、プログラムをオブジェクトが
メッセージを投げ合うという形に表現するパラダイムです。
オブジェクトは内部にデータを保持すると同時に、メッセー
ジを投げられた時にどうしようというプログラムも持ち
...
17 クラスと構造体とオブジェクト指向
C++ には class というキーワードがあります。また C++ にも
struct というキーワードがあります。C++ において両者はほ
とんど同じです。違いは、メンバがデフォルトで private ...
ンスで表すのが C++ 流のオブジェクト指向です。
もう少しまともな例を挙げます。
例えば、libkairo の hal がオブジェクト指向の概念を実装して
います。細かいところを省くと以下のようなコードになって
います。
list 12 hal0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# include <stdio .h>
struct hal_adc {
unsigned int va...
had adc 型の変数がオブジェクトです。このオブジェクトに
電圧をよこせというメッセージを送ることは、
1

unsigned int get_adc_value (const hal_adc *adc);

に hal adc 型の変数を...
18 this ポインタとメンバ関数
これをメンバ変数と同じ感じで呼び出せるようにしようとい
うのが C++ のメンバ関数です。

hal enc を簡略化したものを構造体とクラスで書いて見ま
した。
list 13 hal0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# include <stdio .h>
struct hal_adc {
unsigned int va...
list 14 hal1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# include <cstdio >
class hal_adc {
private :
unsi...
hal enc 型の変数とさっきから書いて来ましたが、正確には
変数の中に入っている値こそが議論の対象です。
これを C++ では hal enc のインスタンスと言います。
内部的にはやってることは構造体の方と同じです。
19 隠蔽と friend
しかし set adc value や get adc value って余計な関数呼び出
しが発生するだけじゃね?って適当な人が直接 value を書き
換えたとします。
これは後々 hal の中身を書き換えたり、不...
list 15 access0.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# include <stdio .h>
struct hal_adc {
unsign...
libkairo だと構造体を hal adc prv.h というヘッダファイルで
定義して、制御屋はそこで定義されてるものは直接使わない
ようにしようという事にしていますが、強制力はありま
せん。
しかし、それを C++ でやってみるとこれは...
list 16 access1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# include <cstdio >
class hal_adc {
priva...
こうして世界は平和になりました。
エラーメッセージを見ると解ると思いますが、仕事してるの
は四行目の private:というアクセス指定子です。次のアクセ
ス指定子までのメンバ変数を public にします。
これによって value は外側か...
また、struct と class の違いはデフォルトのアクセス修飾子
の違いだけですから、これでも同じ事です。
list 17 access2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
でも特別にこの関数にだけは private も触らせてあげようと
いう事もあります。そんな時には friend を使います。
list 18 access3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
20 演習問題
• C で定義した関数を C++ から呼び出してみよ。逆に C++
で定義した関数を C から呼び出してみよ。
• 静的なローカル変数への左辺値参照を関数から返して、呼
び出し元でそれを変更してみよ。
21 Reference
http://www.cplusplus.com/
http://en.cppreference.com/w/
http://www.openstd.org/jtc1/sc22/wg21/docs/papers/201...
22 Lisence
The text of this document is distributed under the Creative
Commons Attribution-ShareAlike 2.0 License.
Upcoming SlideShare
Loading in …5
×

C++ lecture-0

1,045 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,045
On SlideShare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

C++ lecture-0

  1. 1. C++ 言語講習会第 0 回資料 書いた人:@sunaemon0(回路屋 2 年)
  2. 2. 1 目標 • linukairo のソースコードを読むことが出来るようになる • 効率的で安全なコードを書くための基礎知識を身につける – 例外安全性を保つ – 資源管理をおこなう – 未定義動作を避ける
  3. 3. 2 C++ ことはじめ C++ は、C++98, C++03, C++11 と進化してきました。 C も K&R, C89, C99, C11 と進化してきました。
  4. 4. 初期においては C++ は C の上位互換だったのですが、今では そうではありません。 ただ、libkairo で使われている構文なら、ほとんどが C++ で もそのまま動きます。 今回の講習会では C89 の知識を前提とした上で、C++03 お よび C++11 はそれよりどのように違うのかという事を説明 していく予定です。
  5. 5. 3 コンパイルの方法について この pdf ファイルに同梱されているソースコードは以下のよ うにコンパイルできます。 1 # g++ --std=c++11 example0 .cpp -W -Wall
  6. 6. 4 C89 と C++03 の違い 1 • 一行コメントが追加された。 //コレです • 関数内のブロックで先頭以外でも宣言を行えるように なった。 • stdint.h がある。 • bool 型がある。 以上は C99 に入っているので使ったことのあるひとも多い でしょう。
  7. 7. list 1 example0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # include <stdio .h> int main(int , char **) // you can omit unused variable { printf ("input values :"); int val [10]; // declears variable after function call for (int i = 0; i < 10; i++) // declears variable in the init expression scanf ("%d", val + i); for (int i = 0; i < 10; i++) printf ("%d ", val[i]); printf ("n"); return 0; }
  8. 8. 5 C++03 と C89 との違い 2 • 構造体を使いやすくするのに、C では typede をよく使い ましたが、typedef なしで同じことができるようになりま した。 • 引数を取らない関数について、int f(void) でなく、int f() と 宣言することになりました。C の int f() のように引数を具 体的に指定しない宣言はできなくなりました。
  9. 9. 6 iostream 詳しいことは次回やりますが、C++ では printf/scanf の代わ りに次のように書きます。 list 2 iostream0.cpp 1 2 3 4 5 6 7 8 9 # include <string > # include <iostream > int main(int , char **) { std :: string s; std :: cin >> s; // read from standard input std :: cout << s << std :: endl; // write to standard output std :: cerr << "no error" << std :: endl; // write to standard error output } std::cout << なんちゃらで printf 的なことができるという認 識で実用上問題ありません。 std::string というのは可変長の文字列を保持できる型です。
  10. 10. 以降、とりあえず::について解説します。
  11. 11. 7 名前空間 C++ ではライブラリの間の名前の衝突を防止するため、 namespace というキーワードを使って名前空間を分離する ことができるようになっています。それぞれの名前空間にア クセスするには::演算子を使います。
  12. 12. list 3 namespace.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # include <iostream > namespace foo { int f() { return 1;} } namespace bar { int f() { return 2;} } int main(int , char **) { std :: cout << foo ::f() << std :: endl; // 1 std :: cout << bar ::f() << std :: endl; // 2 // std :: cout << f() << std :: endl; // f was not declared in this scope return 0; }
  13. 13. 大域スコープにはこのようにアクセスします。 list 4 namespace1.cpp 1 2 3 4 5 6 7 8 9 10 11 # include <iostream > int f() { return 1;} int main(int , char **) { std :: cout << ::f() << std :: endl; std :: cout << f() << std :: endl; return 0; } // 1 // 1
  14. 14. 8 using いちいち名前空間を指定するのが面倒な場合、using ディレ クティブを使って名前空間を指定しなかった時に検索する名 前空間を登録することができます。 この際、関数名が衝突して曖昧さが発生した場合は、その関 数の使用時にコンパイルエラーが出ます。
  15. 15. list 5 using0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # include <iostream > namespace foo { int f() { return 1;} } namespace bar { int f() { return 2;} } using namespace foo; int main(int , { std :: cout std :: cout std :: cout return 0; } char **) << foo ::f() << std :: endl; // 1 << bar ::f() << std :: endl; // 2 << f() << std :: endl; // 1
  16. 16. 9 Koenig Lookup(飛ばして良い)
  17. 17. このコードはコンパイルエラーが出ます。 list 6 koenig0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # include <iostream > struct test {int a;}; namespace foo { int f(test dummy ) { return 1;} } int main(int , char **) { test tmp; std :: cout << f(tmp) << std :: endl; // f was not declared in this scope return 0; }
  18. 18. これだとコンパイルできます。 list 7 koenig1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # include <iostream > namespace foo { struct test {int a;}; int f(test dummy ) { return 1;} } int main(int , char **) { foo :: test tmp; std :: cout << f(tmp) << std :: endl; // ok! return 0; }
  19. 19. どうして2つ目の例だとうまく行くかというと、C++ のコン パイラはスコープ指定子がない関数の呼び出しが有った場合 に、大域スコープと無名スコープ、using ディレクティブで 導入されたスコープ、using で導入されたシンボル以外にも、 引数の型が属する名前空間も検索するからです。 このように引数の型に依存した検索を行うことを、 ADL(Argument Dependent Lookup) または、Konig Lookup と言います。
  20. 20. 10 標準ライブラリについて c++03 からは c および c++ のライブラリを読み込むときに 1 2 # include <stdio .h> # include <iostream .h> でなく、 1 2 # include <cstdio > # include <iostream > とします。後者の方が新しい書き方です。 前者だと、それぞれのシンボルは大域名前空間に入っていま すが、後者では std 名前空間に入っています。
  21. 21. 11 関数のオーバーロード c++ では引数の型が違う同名の関数を定義できます。 list 8 overload0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # include <stdio .h> int f(int c) { return 1;} int f(char i) { return 2;} int f(int i, char d) { return 3;} int main(int , char **) { char c = 0; int i = 0; std :: cout << f(i) << std :: endl; // 1 std :: cout << f(c) << std :: endl; // 2 std :: cout << f(c,c) << std :: endl; // 3 return 0; }
  22. 22. 型がぴったり合う関数定義があれば、それが呼ばれます。型 変換を行うことで呼び出せる関数があれば、それが呼ばれ ます。
  23. 23. 12 extern “C” gcc でコンパイルすると、.o ファイルのシンボル名と、C で 書いた関数名が一致します。nm a.out として調べてみると わかり安いです。やったことがある人なら解ると思います が、違う翻訳単位で同名の関数を定義するとリンクするとき に怒られます。
  24. 24. C++ では同名でも型の違う関数が定義できます。これでな んで ld に怒られないかというと単純にシンボル名に型の情報 を含めているためです。 例えば、 1 2 3 4 can_packet make_can_motor_packet (uint8_t , uint16_t , motor_mode ); // _ZN12liblinukairo21make_can_motor_packetEhtNS_10motor_modeE std :: string get_short_name (bool , uint8_t ); // _ZN12liblinukairo14get_short_nameEbh みたいな感じです。c++filt というコマンドを使うとこの暗号 を解読できます。
  25. 25. しかし、c で書かれたライブラリと c++ で書いたコードをリ ンクするときにこの機能が悪さをします。 1 int getchar (); みたいにしても変な名前のシンボル Z7getcharv を探しに 行ってしまってリンクがうまく出来ません。代わりに 1 2 3 4 extern "C" { int getchar (); } とすることでちゃんと getchar というシンボルを呼び出すの でうまく行きます。
  26. 26. 13 スタックとヒープ ローカル変数や呼び出し番地などが格納される領域をスタッ ク領域と言います。 C で言えば、malloc で取って来れる領域のことをヒープ領域 と言います。 static 変数が配置される領域を静的領域といいます。
  27. 27. 14 new と delete c++ ではヒープ領域に変数を確保するのに new を使います。 開放するには、delete を使います。 list 9 new0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 # include <cstdio > int main(int , char **) { int *i = new int; std :: scanf("%d", i); std :: printf ("%dn", *i); delete i; return 0; }
  28. 28. 配列を確保する場合は new[] を使います。また開放するには delete[] を使います。 list 10 new1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 # include <cstdio > int main(int , char **) { char *s = new char [100]; std :: scanf("%s", s); std :: printf ("%sn", s); delete [] s; return 0; }
  29. 29. new/delete は malloc/free に比べてキャストが必要がないと いう点で型安全になっています。 開放するときに使う演算子が違うことに注意して下さい!
  30. 30. 15 左辺値参照とポインタ C++ では実はポインタはあまり使いません。 C でポインタを使う場面は多分この4つです。 1. 2. 3. 4. 参照渡しをする。 malloc を使ってヒープ領域に動的に領域を確保する。 文字列を扱う。(2. の特殊例) (回路レイヤだけですが) 定数からポインタにキャストして レジスタをゴリゴリする。
  31. 31. 今回やる左辺値参照というのはざっくり言うと参照渡しをす るためのものです。 次回以降やりますが、文字列は std::string を使い、ヒープ領 域に配列を確保するには普通 std::vector を使います。 C++ でポインタが必要になるのは、this ポインタを使うと き、C で書かれたライブラリをいじるとき、それとメモリを 直接ゴニョゴニョするときくらいです。
  32. 32. 実引数を関数に渡すときに、値をコピーして渡すのを値渡し と言います。 • 呼び出された側は、実引数を変更できません。 • 大きなデータを値渡しすると、大きなコストがかかり ます。 実引数を関数に渡すときに、値をコピーせずそのまま渡すの を参照渡しと言います。 • 呼び出された側は、実引数を変更できます。 • 大きなデータを参照渡ししても、コストは小さいデータと 変わりません。
  33. 33. ポインタで参照渡しを行うと以下の問題があります。 • 値渡しの時と記法が違う (同時に利点でもあります。) • NULL を渡せてしまう これらを解消したのが左辺値参照です。
  34. 34. 左辺値参照は基本的にはポインタです。 宣言は 1 2 int a = 0; // local variable int &r = a; // reference of a このように行います。 左辺値参照はこのように、lvalue(ローカル変数などの具体的 に名前のついた値) で初期化しないと作れません。 1 2 3 4 5 6 7 int &r; // ‘’ r declared as reference but not initialized int &e = 1; // invalid initialization of non - const reference of type // ‘ int ’ & from an rvalue of type ‘’ int const int &f = 1; // ok 歴史的経緯から、定数左辺値参照はなんと領域を確保するの
  35. 35. で、rvalue(リテラルなどの名前のついていない一時的な値) で初期化できます。 これはポインタとは全く違った振る舞いです。
  36. 36. 参照渡ししてみましょう。 list 11 reference0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # include <cstdio > void f(int &i) { i=3; } int main(int argc , char ** argv) { int i=0; f(i); std :: printf ("%dn", i); return 0; } ポインタみたいに 1 *i = 3;
  37. 37. とは書きません。参照はその元の変数と同じように振る舞い ます。 そのため、ポインタと違って free() や delete はできません。
  38. 38. 16 オブジェクト指向 オブジェクト志向というのは、プログラムをオブジェクトが メッセージを投げ合うという形に表現するパラダイムです。 オブジェクトは内部にデータを保持すると同時に、メッセー ジを投げられた時にどうしようというプログラムも持ち ます。
  39. 39. 17 クラスと構造体とオブジェクト指向 C++ には class というキーワードがあります。また C++ にも struct というキーワードがあります。C++ において両者はほ とんど同じです。違いは、メンバがデフォルトで private に なるか public かというだけです。public とか private について は後でやります。 そこで、以降 struct の C 的な用法を構造体、class の C++ 的 な用法をクラスと呼びます。 クラスというのは、C++ においては構造体にメンバ関数やカ プセル化やコンストラクタ・デストラクタや継承や仮想関数 などの概念を付け加えたものです。 オブジェクト指向でいうオブジェクトを、クラスのインスタ
  40. 40. ンスで表すのが C++ 流のオブジェクト指向です。
  41. 41. もう少しまともな例を挙げます。 例えば、libkairo の hal がオブジェクト指向の概念を実装して います。細かいところを省くと以下のようなコードになって います。
  42. 42. list 12 hal0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # include <stdio .h> struct hal_adc { unsigned int value ; }; void set_adc_value ( hal_adc *adc , unsigned int value ) { adc -> value = value ; } unsigned int get_adc_value (const hal_adc *adc) { return adc -> value ; } int main(int , char **) { hal_adc adc; set_adc_value (&adc , 3); printf ("%dn", get_adc_value (& adc)); return 0; }
  43. 43. had adc 型の変数がオブジェクトです。このオブジェクトに 電圧をよこせというメッセージを送ることは、 1 unsigned int get_adc_value (const hal_adc *adc); に hal adc 型の変数を渡して呼び出すという事に相当し ます。 C++ のクラスは、これの書き方を書き換えたものがベースに なっています。
  44. 44. 18 this ポインタとメンバ関数 これをメンバ変数と同じ感じで呼び出せるようにしようとい うのが C++ のメンバ関数です。 hal enc を簡略化したものを構造体とクラスで書いて見ま した。
  45. 45. list 13 hal0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # include <stdio .h> struct hal_adc { unsigned int value ; }; void set_adc_value ( hal_adc *adc , unsigned int value ) { adc -> value = value ; } unsigned int get_adc_value (const hal_adc *adc) { return adc -> value ; } int main(int , char **) { hal_adc adc; set_adc_value (&adc , 3); printf ("%dn", get_adc_value (& adc)); return 0; }
  46. 46. list 14 hal1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # include <cstdio > class hal_adc { private : unsigned int value ; public : unsigned int get_value () { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } }; int main(int , char **) { hal_adc adc; adc. set_value (3); std :: printf ("%dn", adc. get_value ()); return 0; } これをみれば、this ポインタというのがなにか分かると思い ます。
  47. 47. hal enc 型の変数とさっきから書いて来ましたが、正確には 変数の中に入っている値こそが議論の対象です。 これを C++ では hal enc のインスタンスと言います。 内部的にはやってることは構造体の方と同じです。
  48. 48. 19 隠蔽と friend しかし set adc value や get adc value って余計な関数呼び出 しが発生するだけじゃね?って適当な人が直接 value を書き 換えたとします。 これは後々 hal の中身を書き換えたり、不正な値が代入され ないかチェックしたりということができなくなるのでよくあ りません。 しかし、C のコードだと結構簡単にできてしまいます。
  49. 49. list 15 access0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # include <stdio .h> struct hal_adc { unsigned int value ; }; void set_adc_value ( hal_adc *adc , unsigned int value ) { adc -> value = value ; } unsigned int get_adc_value (const hal_adc *adc) { return adc -> value ; } int main(int , char **) { hal_adc adc; adc. value = 3; // set_adc_value (&adc , 3); printf ("%dn", adc. value); // printf ("%dn", get_adc_value (& adc)); }
  50. 50. libkairo だと構造体を hal adc prv.h というヘッダファイルで 定義して、制御屋はそこで定義されてるものは直接使わない ようにしようという事にしていますが、強制力はありま せん。 しかし、それを C++ でやってみるとこれはコンパイルが通 りません。
  51. 51. list 16 access1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # include <cstdio > class hal_adc { private : unsigned int value ; public : unsigned int get_value () { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } }; int main(int , char **) { hal_adc adc; adc. value = 3; // ‘ unsigned int hal_adc :: ’ value is private std :: printf ("%dn", adc.value); // ‘ unsigned int hal_adc :: ’ value is private return 0; }
  52. 52. こうして世界は平和になりました。 エラーメッセージを見ると解ると思いますが、仕事してるの は四行目の private:というアクセス指定子です。次のアクセ ス指定子までのメンバ変数を public にします。 これによって value は外側からは直接触れなくなります。こ れを隠蔽と言います。 逆に get value は public というアクセス修飾子がついている ので外側からアクセスできます。 先述の通り class のデフォルトのアクセス修飾子は private な ので本当は四行目は要りません。
  53. 53. また、struct と class の違いはデフォルトのアクセス修飾子 の違いだけですから、これでも同じ事です。 list 17 access2.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # include <cstdio > struct hal_adc { unsigned int get_value () { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } private : unsigned int value ; }; int main(int , char **) { hal_adc adc; adc. value = 3; // ‘ unsigned int hal_adc :: ’ value is private std :: printf ("%dn", adc.value); // ‘ unsigned int hal_adc :: ’ value is private return 0; }
  54. 54. でも特別にこの関数にだけは private も触らせてあげようと いう事もあります。そんな時には friend を使います。 list 18 access3.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # include <cstdio > struct hal_adc { unsigned int get_value () { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } private : unsigned int value ; friend int main(int argc , char ** argv); }; int main(int , char **) { hal_adc adc; adc. value = 3; // ok std :: printf ("%dn", adc.value); // ok }
  55. 55. 20 演習問題 • C で定義した関数を C++ から呼び出してみよ。逆に C++ で定義した関数を C から呼び出してみよ。 • 静的なローカル変数への左辺値参照を関数から返して、呼 び出し元でそれを変更してみよ。
  56. 56. 21 Reference http://www.cplusplus.com/ http://en.cppreference.com/w/ http://www.openstd.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
  57. 57. 22 Lisence The text of this document is distributed under the Creative Commons Attribution-ShareAlike 2.0 License.

×