inline関数をヘッダに書く際の注意点
inlineメンバ関数を含むクラスを作成するときにヘッダと実装ファイルを作成する方法について。
下のように、2つのcppファイルで利用するクラスAを作成するとしましょう。
main.cpp
#include "A.h" int main(){ A a; a.method(); return 0; }
A.h | A.cpp |
#ifndef AUser_H #define AUser_H #include "A.h" class AUser { public: AUser(); }; #endif | #include "AUser.h" AUser::AUser() { A a; a.method(); } |
この状況でコンパイルが通るようにするためには、下の例のようにA.hとA.cppを普通に作ればOKです。
Example1
A.h | A.cpp |
#ifndef A_H #define A_H class A { public: void method(); }; #endif | #include "A.h" void A::method(){ } |
ここでmethod()をinline関数を使って定義するにはどうすればよいか考えてみます。
下の例ではヘッダにinline関数の定義がないので、A::method()が存在しないことになってしまいます。
そのためmain.obj,AUser.objの2つでリンクエラーが発生します。
Example2 --compile error(未解決の関数名)
A.h | A.cpp |
#ifndef A_H #define A_H class A { public: void method(); }; #endif | #include "A.h" inline void A::method(){ } |
Example4のようにクラス宣言の内部に記述して対処することができます。クラス内でメンバ関数を定義すると自動的にinline化されるので、"inline"修飾子を省略することができます。
Example3 |
Example4 |
#ifndef A_H #define A_H class A { public: void method(); }; inline void A::method() { } #endif | #ifndef A_H #define A_H class A { public: void method(){ } }; #endif |
そうしないと、main.objとAUser.objの両方でmethod()の定義を行うことになってしまいます。
Example5はコンパイルエラーで、Example6はコンパイルが通ります。
Example5 --compile error(複数の関数定義)
A.h
#ifndef A_H #define A_H class A { public: A(); void method(); }; A::A(){ } inline void A::method() { } #endif
Example6
A.h | A.cpp |
#ifndef A_H #define A_H class A { public: A(); void method(); }; inline void A::method() { } #endif | #include "A.h" void A::A(){ } |
特定の関数はinline化してはいけない場合はExample6のように実装の一部を.cppに記述することになります。
ただそういう制約がなくて、実装部分を全てヘッダ一つだけにまとめたいという場合は、
他のメンバ関数も全てinline関数にすると、ヘッダにまとめることができます。(Example7,8どちらでもOK)
Example7のように、宣言と定義を分離して、実装部分にinlineを付けるのが良いかもしれません。
Example7 |
Example8 |
#ifndef A_H #define A_H class A { public: A(); void method(); }; inline A::A(){ } inline void A::method() { } #endif | #ifndef A_H #define A_H class A { public: A(){} void method(); }; inline void A::method() { } #endif |