4.3.3 グローバル変数とstatic変数
グローバル変数は、main()関数が始まる前から存在し、main関数が終了した後に破棄される。
#include <iostream>
class A
{
public:
A();
~A();
};
A::A(){
std::cout << "consuructor" << std::endl;
}
A::~A()
{
std::cout << "desstructor" << std::endl;
}
// Before main() is called, this constructor is called.
// After main() finishes, this destructor is called.
A a;
int main(){
std::cout << "main() function" << std::endl;
}
- static変数
ローカル変数をstatic変数にすると、スコープから抜けても破棄されず
プログラムが終了するまで残る。
グローバル変数をstaticにすると、ファイルスコープなグローバル変数になる。
#include <iostream>
int count(){
// static変数になっているローカル変数は、寿命が長い以外は、一緒なので、有効スコープからしか
// アクセスできない。
static int counter = 0;
return ++counter;
}
int main(){
std::cout << "1回目: " << count() << std::endl;
std::cout << "2回目: " << count() << std::endl;
std::cout << "3回目: " << count() << std::endl;
}
1回目: 1
2回目: 2
3回目: 3
4.3.4 ダングリングポインター
#include <iostream>
int* dangling_pointer(){
int i = 0;
return &i;
}
int main(){
int*p = dangling_pointer();
// 寿命が尽きたローカル変数を変更しようとしている。
*p = 42;
}
4.4 初期化構文付き条件分岐
4.4.1 初期化構文付きif文
#include <iostream>
int foo(int value){
std::cout << "foo " << value << std::endl;
return value;
}
bool is_even(int value){
return value%2 == 0;
}
bool is_zero(int value){
return value == 0;
}
int main(){
if(int result = foo(42); is_even(result) && !is_zero(result)){
std::cout << "foo(42)は0ではない偶数を返しました" << std::endl;
}
}
4.5 分割コンパイル
4.5.1 コンパイル
ソースファイルを実行形式ファイルに変換する手順をコンパイルという。
4.5.2 プリプロセス
→コンパイルの直前に行われる、ヘッダーファイルを読み込んだり、マクロの展開をしたり。
4.5.3 リンクとビルド
ビルド: 複数のソースファイルから1つのプログラムを作り上げる手順のこと。
リンク: オブジェクトファイルを結合すること。

- Main.cpp
# include "test2.hpp"
int main(){
show_value(42);
}
- Test2.hpp
void show_value(int value);
- Test2.cpp
#include "test2.hpp"
#include <iostream>
void show_value(int value){
std::cout << "value=" << value << std::endl;
}
4.5.4 extern変数
ヘッダーファイルや複数のソーづファイルでグローバル変数を
宣言してしまうと、それぞれのソースファイルがコンパイルされた時に
それぞれのオブジェクトファイルがグローバル変数の実態を持ってしまう
原因: 変数宣言が暗黙のうちに変数定義まで含んでいる
変数宣言の際に、Externキーワードを使うとそのグローバル変数の実体はどこか
別のところで定義されてるものとして扱う。
→extern変数
-
ただしこれはグローバルスコープの変数でしか宣言できない。
-
Test2.cpp
# include <iostream>
int value = 42;
void show_extern_variable(){
std::cout << "extern varibale's address: " << &value << std::endl;
std::cout << "extern varibale's value: " << value << std::endl;
}
- Main.cpp
# include <iostream>
extern int value;
void show_extern_variable();
int main(){
std::cout << "main: extern変数のアドレス" << &value << std::endl;
std::cout << "main: extern変数の値" << value << std::endl;
value = 0;
show_extern_variable();
}
4.5.5 staticメンバー変数の定義
Staticメンバー変数だけは、クラス定義時だけの宣言では不十分で、
クラスの定義の外側で別途定義が必要だった。
定義をヘッダーファイルに書いちゃうと、複数のファイルで実体が発生しちゃうから
どこか一つのソースファイルでのみ定義する必要がある。
4.6 インライン関数
4.6.1 ヘッダーファイルに定義された関数のインライン展開
ヘッダーに関数定義を書くことができれば、コンパイラはその関数本体を呼び出しもとにインライン展開する。
4.6.2 インライン指定
ヘッダーファイルに関数の宣言と定義を両方行うために必要。
あるヘッダーファイルが、複数のソースファイルにインクルードされている場合、そのヘッダーファイルにODR(単一定義規則)というルールに違反する。インライン指定を行うとこの問題を解決してくれるようコンパイラーが調整してくれる。
4.6.3 自動インライン化
クラスの定義時にメンバー関数を宣言と同時に定義すると、それは自動的にインライン関数となるのでODRに反しない。
#include <iostream>
class A
{
int i;
public:
A() : i(0) {};
void set_i(int i)
{
this->i = i;
}
void show() const
{
std::cout << i << std::endl;
}
};
int main(){
A a;
a.show();
a.set_i(123);
a.show();
}
↑これは自動的にインライン化される。
- intとかの変数は?
int などの組み込み型の宣言は自動的に定義されます。これは、コンパイラが割り当てられる領域の量を認識するためです。
4.6.4 宣言のみのメンバー関数をinlineにする
クラス定義でメンバー関数の宣言はしたものの、定義までは行わない場合、そのメンバー関数は自動でインライン化されない
#include <iostream>
class A{
int i;
public:
// コンストラクターをインライン指定付きで宣言
inline A();
// 自動でインラインになるメンバー関数
void set_i(int i){
this->i = i;
};
// インラインにならないメンバー関数
void show() const;
};
A::A():i(0){}
void A::show() const {
std::cout << i << std::endl;
}
int main(){
A a;
a.show();
a.set_i(123);
a.show();
}
4.7 名前空間
4.7.1 名前空間とスコープ解決演算子
#include <iostream>
namespace A{
struct A{
int a;
};
void foo(){
std::cout << "A::foo()" << std::endl;
}
}
namespace B{
struct S{
int b;
};
void foo(){
std::cout << "B::foo()" << std::endl;
}
}
int main(){
A::foo();
B::foo();
A::A as;
B::S bs;
}
4.7.2 ネストした名前空間
namespace library{
namespace module{
namespace detail{
void internal_function();
}
}
}
namespace library::module::detail
4.7.3 名前空間の省略
名前検索の手順:
使う側が所属する名前空間を基点として、上位の名前空間へと1階層ずつ順番に、再起的に探索していく。グローバル名前空間まで探索して見つからなかった場合にはエラーになる。
void featureX();
void featureY();
void featureZ();
namespace submodule{
void featureX();
void featureY();
void featureZ();
}
namespace module_a{
void featureX();
void featureY();
namespace submodule{
void featureX();
void featureY();
}
}
namespace module_b{
void featureX();
void featureY();
namespace submodule{
void featureX();
void featureY();
}
namespace A{
void featureX();
namespace submodule{
void featureY();
}
void caller(){
featureX(); // module_b::A::featureX
submodule::featureX(); // module_b::A::submodule::featureX
featureY(); // module_b::featureY
submodule::featureY(); // module_b::submodule::featureY
featureZ(); // global名前空間のfeatureZ
submodule::featureZ(); // submodule::featureZ
}
}
}
らしいけどなんかerror出るな、C++のコンパイラのversionによって違う?
4.7.4 無名名前空間
練習問題4.7
#include <iostream>
namespace module {
int increment(int a){
return a + 2;
}
}
int increment(int a){
return a + 3;
}
int main(){
int b = module::increment(2);
int c = increment(2);
std::cout << b << std::endl;
std::cout << c << std::endl;
}
#include <iostream>
namespace module {
int increment(int a){
return a + 2;
}
}
int increment(int a){
return a + 3;
}
int main(){
using module::increment;
int b = module::increment(2);
int c = increment(2);
std::cout << b << std::endl;
std::cout << c << std::endl;
}
4.8 リンケージ
4.8.1 C言語とC++
リンケージ: C++からC言語の関数を呼ぶ場合とC言語からC++の関数を呼べるようにする.
C言語にリンケージの機能がないので、C++からCを呼ぶ場合も、CからC++を呼ぶ場合もどちらもC++の方で指定する.
call_cpp.cpp
#include <iostream>
// C言語を呼ぶ
extern "C" void call_c();
// C言語から呼ばれる
extern "C" void call_cpp(){
std::cout << "call_cpp" << std::endl;
call_c();
}
main.c
#include <stdio.h>
void call_c(void)
{
puts("call_c");
}
void call_cpp(void);
int main(){
call_cpp();
}
4.9 プリプロセッサー
4.9.1 プリプロセッサー命令
#include <file-name> // 標準ライブラリ
#include "file-name" // プロジェクトの中で用意したヘッダーファイルとか
4.9.2 マクロ
#include <iostream>
void hello(){
std::cout << "hello, world" << std::endl;
}
void goodbye(){
std::cout << "goodbye world" << std::endl;
}
int main(){
hello();
std::cout << "hello macro" << std::endl; // 文字列は置き換えない.
# define hello goodbye
hello();
std::cout << "hello, macro" << std::endl;
}
- 関数形式マクロ
#include <iostream>
void hello(){
std::cout << "hello, world" << std::endl;
}
# define id(name) name
int main(){
id(hello)();
}
4.9.4 #if命令
プリプロセッサーの役割
→特定の条件でプログラムを有効にしたり無効にしたりする.
- LINE
- FILE
- _cplusplus
はマクロとして定義されているためプリプロセッサーの条件として使うことができる.
#include <iostream>
#define HOGE
int main()
{
#if defined(HOGE)
std::cout << "defined(HOGE)はtrueです." << std::endl;
#else
std::cout << "defined(HOGE)はfalseです." << std::endl;
#endif
}
- インクルードガード
#ifndef UNIQUE_IDENTIFIER
#define UNIQUE_IDENTIFIER
#include <iostream>
namespace A::B::C{
void message(){
std::cout << "message" << std::endl;
}
}
int main(){
A::B::C::message();
}
# endif