1

ヘッダーに変数を定義して、複数のファイルで使用できるようにしたいです。たとえば、変数aをどこかに定義し、p1.cpp と p2.cpp の両方で使用/変更できるようにする

これは、私がやろうとしていることの 3 つの単純なファイルの例です。

// vars.hpp
#ifndef VARS_HPP
#define VARS_HPP

int a = 1;
float b = 2.2;
void double_vars();

#endif



// vars.cpp
#include "vars.hpp"
void double_vars () {
    a *= 2;
    b *= 2;
}

// vars_main.cpp
#include <cstdio>
#include "vars.hpp"
int main () {
    printf("a: %d; b: %f\n", a, b);
    double_vars();
    printf("a: %d; b: %f\n", a, b);
}

ここで、上記を次のようにコンパイルします。

g++ -Wall -W -g vars.cpp vars_main.cpp -o vars && ./vars

次のエラーが表示されます。

/tmp/ccTPXrSe.o:(.data+0x0): multiple definition of `a'
/tmp/ccnc1vof.o:(.data+0x0): first defined here
/tmp/ccTPXrSe.o:(.data+0x4): multiple definition of `b'
/tmp/ccnc1vof.o:(.data+0x4): first defined here

なぜこれが起こるのか誰かが私に説明できますか?私はヘッダーファイルにガードを持っているので、私が理解している限り、それは一度だけ含める必要があるため、複数の宣言があってはなりません

4

3 に答える 3

4

まず、これをしないでください。グローバルな可変状態は通常悪いことです。

問題は、リンカがインクルード ステートメント内のファイルの結合を完了すると、これが main.cpp で 1 回、vars_main.cpp で 1 回、2 回見られることです。

int a = 1;
float b = 2.2;

これをヘッダー ファイルにのみ配置すると、ヘッダーを含むファイルごとに 1 回、複数の翻訳単位で表示さcppれます。コンパイラが、あなたが話している a と b を知ることは不可能です。

externこれを修正する方法は、ヘッダーでそれらを宣言することです。

extern int a;
extern float b;

これは、a と b がコードのこの部分ではなく、どこかで定義されていることをコンパイラに伝えます。

次に、cpp ファイルの 1 つでそれらを定義します。

int a = 1;
float b = 2.2;

aこうすることで、格納場所と格納場所が明確に定義された場所が 1 つbになり、コンパイラはドットを適切に接続できます。

于 2012-11-03T13:12:33.987 に答える
4

ヘッダー ファイルに、次のように記述します。

extern int a;
extern float b;

これにより、変数が宣言されます。

CPP ファイルの 1 つに、次のように記述します。

int a = 1;
float b = 2.2;

これにより、変数が定義されます。変数が一度だけ定義されている限り、どの CPP ファイルに定義を入れてもかまいません。

于 2012-11-03T13:09:49.483 に答える
1

Collin はすでに問題と解決策を説明しましたが、これがいかに悪いスタイルであるかを強調したいと思います。

この単純な演習からもわかるように、この方法でグローバルを使用することはしばしば悪いことです。すでに見つかったコンパイラの問題とは別に、グローバルを使用して関数間で状態を共有すると、次の問題が発生します。

  • 関数はそれらのシンボルの存在を想定しているため、関数自体を他のプログラムで再利用するのが難しくなります。

  • 1 つの関数を変更すると、その状態を共有する他の関数で問題が発生する可能性があります (つまり、変更は関数に限定されません)。

  • 誰でもいつでも変更できるためa、コードのデバッグが難しくなります。b

理想的には、関数は、パラメーター、戻り値、および例外 (該当する場合) を介して排他的に通信する必要があります。状態を共有することが正しい答えである場合もありますが、それらは比較的少数であり、その中間です。

明らかに C++ コンパイラを使用しているので、ポインターの代わりに参照を使用して、C++ の方法でこれを行います。また、代わりにC++iostreamルーチンを使用しますcstdio(C または C++ のいずれかを記述します。この 2 つを混在させようとすると、胸やけが起こります)。

vars.hpp:

#ifndef VARS_H  // Include guard to prevent multiple inclusion
#define VARS_H

void double_vars(int &, float &);

#endif

vars.cpp:

#include "vars.hpp"

void double_vars(int &x, float &y)
{
  x *= 2;    
  y *= 2.0;  
}

main.cpp:

#include <iostream>
#include "vars.hpp"

int main(void)
{
  int a = 1;
  float b = 2.2;

  std::cout << "a: " << a << "; b: " << b << std::endl;
  double_vars(a, b);
  std::cout << "a: " << a << "; b: " << b << std::endl;

  return 0;
}

xおよびは ( を使用して)参照yとして宣言されているため、およびinへの書き込みは、およびinへの書き込みと同等です。 & xydouble_varsabmain

これを行う C の方法は、参照の代わりにポインターを使用することです。

vars.h:

#ifndef VARS_H  // Include guard to prevent multiple inclusion
#define VARS_H

void double_vars(int *, float *); 
#endif

vars.c:

#include "vars.h"

void double_vars(int *x, float *y)
{
  *x *= 2;    
  *y *= 2.0;  
}

main.c:

#include <stdio.h>
#include "vars.h"

int main(void)
{
  int a = 1;
  float b = 2.2;

  printf("a: %d; b: %f\n", a, b);
  double_vars(&a, &b);
  printf("a: %d; b: %f\n", a, b);
  return 0;
}

この場合、参照の代わりにポインターを使用しているため、 &aの結果をと&bに渡す必要がありdouble_varsます。関数内で、式*xandに書き込むことは、 and に書き込むことと*y同じです。 abmain

于 2012-11-03T15:08:10.433 に答える