Weitere ähnliche Inhalte Ähnlich wie C++ lecture-0 (20) C++ lecture-02. 1 目標
• linukairo のソースコードを読むことが出来るようになる
• 効率的で安全なコードを書くための基礎知識を身につける
– 例外安全性を保つ
– 資源管理をおこなう
– 未定義動作を避ける
3. 2 C++ ことはじめ
C++ は、C++98, C++03, C++11 と進化してきました。
C も K&R, C89, C99, C11 と進化してきました。
4. 初期においては C++ は C の上位互換だったのですが、今では
そうではありません。
ただ、libkairo で使われている構文なら、ほとんどが C++ で
もそのまま動きます。
今回の講習会では C89 の知識を前提とした上で、C++03 お
よび C++11 はそれよりどのように違うのかという事を説明
していく予定です。
6. 4 C89 と C++03 の違い 1
• 一行コメントが追加された。 //コレです
• 関数内のブロックで先頭以外でも宣言を行えるように
なった。
• stdint.h がある。
• bool 型がある。
以上は C99 に入っているので使ったことのあるひとも多い
でしょう。
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. 5 C++03 と C89 との違い 2
• 構造体を使いやすくするのに、C では typede をよく使い
ましたが、typedef なしで同じことができるようになりま
した。
• 引数を取らない関数について、int f(void) でなく、int f() と
宣言することになりました。C の int f() のように引数を具
体的に指定しない宣言はできなくなりました。
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 というのは可変長の文字列を保持できる型です。
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;
}
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
20. 10 標準ライブラリについて
c++03 からは c および c++ のライブラリを読み込むときに
1
2
# include <stdio .h>
# include <iostream .h>
でなく、
1
2
# include <cstdio >
# include <iostream >
とします。後者の方が新しい書き方です。
前者だと、それぞれのシンボルは大域名前空間に入っていま
すが、後者では std 名前空間に入っています。
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;
}
23. 12 extern “C”
gcc でコンパイルすると、.o ファイルのシンボル名と、C で
書いた関数名が一致します。nm a.out として調べてみると
わかり安いです。やったことがある人なら解ると思います
が、違う翻訳単位で同名の関数を定義するとリンクするとき
に怒られます。
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. しかし、c で書かれたライブラリと c++ で書いたコードをリ
ンクするときにこの機能が悪さをします。
1
int getchar ();
みたいにしても変な名前のシンボル Z7getcharv を探しに
行ってしまってリンクがうまく出来ません。代わりに
1
2
3
4
extern "C"
{
int getchar ();
}
とすることでちゃんと getchar というシンボルを呼び出すの
でうまく行きます。
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. 配列を確保する場合は 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;
}
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
歴史的経緯から、定数左辺値参照はなんと領域を確保するの
39. 17 クラスと構造体とオブジェクト指向
C++ には class というキーワードがあります。また C++ にも
struct というキーワードがあります。C++ において両者はほ
とんど同じです。違いは、メンバがデフォルトで private に
なるか public かというだけです。public とか private について
は後でやります。
そこで、以降 struct の C 的な用法を構造体、class の C++ 的
な用法をクラスと呼びます。
クラスというのは、C++ においては構造体にメンバ関数やカ
プセル化やコンストラクタ・デストラクタや継承や仮想関数
などの概念を付け加えたものです。
オブジェクト指向でいうオブジェクトを、クラスのインスタ
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;
}
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. 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 ポインタというのがなにか分かると思い
ます。
48. 19 隠蔽と friend
しかし set adc value や get adc value って余計な関数呼び出
しが発生するだけじゃね?って適当な人が直接 value を書き
換えたとします。
これは後々 hal の中身を書き換えたり、不正な値が代入され
ないかチェックしたりということができなくなるのでよくあ
りません。
しかし、C のコードだと結構簡単にできてしまいます。
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. libkairo だと構造体を hal adc prv.h というヘッダファイルで
定義して、制御屋はそこで定義されてるものは直接使わない
ようにしようという事にしていますが、強制力はありま
せん。
しかし、それを C++ でやってみるとこれはコンパイルが通
りません。
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;
}
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. でも特別にこの関数にだけは 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. 20 演習問題
• C で定義した関数を C++ から呼び出してみよ。逆に C++
で定義した関数を C から呼び出してみよ。
• 静的なローカル変数への左辺値参照を関数から返して、呼
び出し元でそれを変更してみよ。
57. 22 Lisence
The text of this document is distributed under the Creative
Commons Attribution-ShareAlike 2.0 License.