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

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

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

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

Q&A

解決済

4回答

4542閲覧

C++でのファイル分割

退会済みユーザー

退会済みユーザー

総合スコア0

C++

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

0グッド

0クリップ

投稿2016/08/21 23:27

編集2016/08/21 23:53

C++のファイル分割について、重複定義の意味と #ifndef の使い方がよく分かりません。

// main.h int funcA(int x); int funcB(int x);
// main.cc #include <iostream> #include "main.h" using namespace std; int main() { cout << funcA(2) + funcB(2) << endl; return 0; }
// funcA.cc int funcA(int x) { return x + 2; }
// funcB.cc #include "main.h" int funcB(int x) { return funcA(2) + 2; }

2つのファイルがmain.hを読み込んでいます。

具体的にどういった問題が発生するのかと、その問題に対する改善をお願いします。

古いバージョンとの互換性は気にしません。


G++ Apple LLVM version 7.3.0 (clang-703.0.31)

cmake_minimum_required(VERSION 3.6)

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

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

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

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

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

ozwk

2016/08/21 23:49

funcA.ccとB.ccは逆では
退会済みユーザー

退会済みユーザー

2016/08/21 23:53

ありがとうございます!
guest

回答4

0

この場合は、特に問題は起きません(複数のソースファイルから同じヘッダを呼んでも特段の問題はありません)。


ただ、複数のヘッダから同じヘッダを呼び出すと問題となってきます。

C++

1/********** hoge_class.h ***********/ 2 3class HogeClass{ 4 // 中身は省略 5} 6 7/********** foo_lib.h.h ***********/ 8#include "hoge_class.h" 9 10// 後略 11 12/********** bar_lib.h.h ***********/ 13#include "hoge_class.h" 14 15// 後略 16 17/********** ソースファイル ***********/ 18#include "foo_lib.h" 19#include "bar_lib.h" 20 21int main(){ 22 //中身は省略 23}

このように書いた場合、foo_lib.hbar_lib.hの両方を経由してhoge_class.hが2回読みこまれてしまい、クラスが二重定義となってエラーになります。

このように、「同じヘッダが2回展開される」ことによるトラブルを防ぐのがインクルードガードです。

C++

1#ifndef HOGE_CLASS_DEFINED 2#define HOGE_CLASS_DEFINED 3 4class HogeClass{ 5 // 中身は省略 6} 7 8#endif

このようにしておくことで、2回めにhoge_class.hが読み込まれた場合にはすでにHOGE_CLASS_DEFINEDが存在するため、クラス定義自体が無視されるようになります。

なお、現代のほぼすべてのコンパイラが#pragma onceと書けば同じヘッダの読み出しを1回だけにしてくれるので、ややこしければそれを使うのもありです。

投稿2016/08/22 00:22

maisumakun

総合スコア145183

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

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

0

こんにちは。

提示されたソースならば、多重定義の問題は置きないと思います。
しかし、聞きたいことはなんとなく分かります。
恐らく、実体の多重定義の話とインクルード・ガード(#ifndef)の話と思います。この2つは別物です。

まず、コンパイル単位という言葉があります。
簡単に説明すると、コンパイラにソース・ファイルを指定してコンパイルしますが、この時の1つのソース・ファイルが1つのコンパイル単位です。他のソースファイルとは全く独立にコンパイルされると言う意味です。
コンパイルされたプログラムはオブジェクトファイル(.obj等)となり、複数のオブジェクトファイルをリンクして1つの実行形式ファイル(.exe等)が作られます。

異なるコンパイル単位に同じ関数の実体が含まれていると多重定義となり、リンカにてエラーになります。
これが実体の多重定義です。
ヘッダで実体を定義してよく発生させてしまいます。例えば下記です。

main.h

C++

1int funcA(int x) { return x + 2; } 2int funcB(int x) { return funcA(2) + 2; }

main.hは、mainA.ccとfuncB.ccの両方から#includeされているので、funcA()とfuncB()の実体がmain.ccコンパイル単位とfuncB.ccコンパイル単位の両方で実体が定義されてしまい、実体の多重定義となります。


1つのコンパイル単位内で、同じものを定義しても多重定義となる場合があります。(ならないように見える場合もあるのでややこしいのですが、#defineで同じシンボルを定義すると確実に多重定義になります。)
この現象が良く発生するのは、#includeでヘッダをインクルードする場合です。
例えば、main.h内で#include <stdio.h>はよくやると思います。そして、#include "main.h"しているcppファイルでも#include <stdio.h>をやらざる得ないケースは、複雑なプロジェクトの場合よくあります。
実際にやっても問題はでません。それは、インクルード・ガードというテクニックが使われているからです。

main.h

C++

1#ifndef MY_PROJECT_MAIN_H 2#define MY_PROJECT_MAIN_H 3 4#define FOO "foo" 5 6#endif

のような使い方をします。
1つのコンパイル単位内でmain.hが初めて#includeされた時はまだMY_PROJECT_MAIN_Hが定義されていませんので、FOOが定義されます。
main.hが次に#includeされた時は、前回のmain.hの#includeにより、既にMY_PROJECT_MAIN_Hが定義されていますから、#ifndefで中身が処理対象になりませんので、FOOの多重定義にはなりません。

投稿2016/08/22 00:49

Chironian

総合スコア23272

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

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

0

ベストアンサー

「重複定義」とは1つのソースコードにて同じ関数が複数箇所で定義されていることを言います。
たしか、redefinition of XXXXとかでたと思います。

この問題は以下を満たすときに起こります。
・あるヘッダファイル(a.h)に、関数が定義(!=宣言)されている
・別のヘッダファイル(b.h)にて、a.hがincludeされている
・問題のソースファイル(main.cc)にて、a.h, b.hが両方includeされている

// a.h int funcA(int x) {return x+1;} // b.h #include "a.h" int funcB(int x) {return x-1;} // main.cc #include "a.h" #include "b.h" int main(int argc, char* argv[]) { printf("hello"); return 0; }

main.ccのコンパイルにおいてincludeが展開された時、a.hで定義されている関数が2つ現れてしまいます。

// include展開後のmain.cc // #include "a.h" -> int funcA(int x) {return x+1;} // #include "b.h" -> int funcA(int x) {return x+1;} int funcB(int x) {return x-1;} int main(int argc, char* argv[]) { printf("hello"); return 0; }

これを防ぐために、ヘッダファイルに「2回、展開されることを防ぐおまじない」をします。
このおまじないが#ifndefで始まる3行セットです。

// a.h #ifndef A_H_ #define A_H_ int funcA(int x) {return x+1;} #endif //A_H_ // b.h #ifndef B_H_ #define B_H_ #include "a.h" int funcB(int x) {return x-1;} #endif // B_H_ // main.cc #include "a.h" #include "b.h" int main(int argc, char* argv[]) { printf("hello"); return 0; }

机上で展開してみると理解できるかと思いますが、最初のa.hのincludeによってA_H_が定義されるので、2回目にa.hがincludeされる場所ではfuncAの定義が記述されなくなります。

// #include "a.h" -> int funcA(int x) {return x+1;} // #include "b.h" -> // #include "a.h"は、ifndefによって何も無いファイルになる int funcB(int x) {return x-1;} int main(int argc, char* argv[]) { printf("hello"); return 0; }

以上が重複定義の意味と、#ifndefによる回避のおまじないです。
今回のケースでは、b.hに対しておまじないをする必要はありませんが、いざというときのために予めおまじないを入れておくのが慣例です。

投稿2016/08/22 00:37

tnd-.-b

総合スコア247

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

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

退会済みユーザー

退会済みユーザー

2016/08/22 04:55

hファイルはfuncAとfuncBで別々につくるべきですか?
tnd-.-b

2016/08/23 02:03

んー、funcAとfuncBが似たような機能なら、まとめても良いです。 1つの.cに1つの.h、と決めてあったりするので、この辺りはポリシーによります。 少なくとも、funcAとfuncBの役割が明確に異なる(例えば、ファイル出力と計算のようなもの)なら、別々にhファイルを作ったほうがよいです。
guest

0

同一のプロトタイプ宣言であれば複数回書かれてもエラーとはなりません。
変数もextern宣言であれば複数あってもエラーとはなりません。

  • 宣言なので平気
int funcA(int,int); int funcA(int,int); int funcA(int,int); static const int CONSTA; static const int CONSTA; extern int P; extern int P; int main(void){ int P = 10; funcA(P,2); return 0; } int funcA(int x,int y){ return x + y;}
  • 多重定義だからエラー
struct { int A; int B; } struct_x; struct { int A; int B; } struct_x; int funcA(int,int); const int CONSTA = 1; const int CONSTA = 1; enum { ENUMA, ENUMB, ENUMC }; enum { ENUMA, ENUMB, ENUMC }; int P = 10; int P = 10; int main(void){ funcA(P,ENUMB); return 0; } int funcA(int x,int y){ return x + y;} int funcA(int x,int y){ return x + y;}

上の例ではすべてソースで書いていますが、ヘッダに分割していた場合もそのヘッダに定数定義や構造体定義が含まれると、二重にインクルードされるときにエラーとなります。
また、最初の例でも1回書けばいいものをエラーにならないからと何度も書くのは無駄です。

ということで、ヘッダが二重に読み込まれないように、
#ifndef, #define マクロを使ってヘッダが読み込まれたことを定義し、二回以上展開されないようにします。

// sample.h #ifndef _SAMPLE_H_ #define _SAMPLE_H_ 1 struct { int A; int B; } struct_x; int funcA(int,int); const int CONSTA = 1; enum { ENUMA, ENUMB, ENUMC }; #endif
#include <stdio.h> #include "sample.h" // ヘッダ内の #defineがなければ以下はエラー #include "sample.h" #include "sample.h" #include "sample.h" #include "sample.h" int main(void){ int P = 10; funcA(P,ENUMB); return 0; } int funcA(int x,int y){ return x + y;}

投稿2016/08/22 00:16

flied_onion

総合スコア2604

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問