大域な静的変数の参照について

[戻る]
えの 2006/05/07(日) 05:07:30
えのといいます。
VC++2005のstamdardを使っています。

namespaceのNS_Aに、静的変数のVAL1を定義して、
メインから初期化するようにしています。
その値を、NS_Aに含まれるクラスC_Aから参照すると、
なぜかいつもゼロが返ってきてしまいます。

構成はこのような感じです。

NS_A.h・・・VAL1が定義されています
NS_A.cpp・・VAL1を参照します(クラスC_Aのメンバ関数が見ます)
main.cpp・・VAL1に値を設定します(setVAL1を呼びます)

何かヒントになる情報でもございましたら、
お教えください。
よろしくおねがいします。

επιστημη 2006/05/07(日) 10:46:02
ヘッダに定義をかいてはいけません。

--- NS_A.cpp
static int VAL1;
int get() { return VAL1; }

--- main.cpp
static int VAL1;
int main() {
  VAL1 = 123;
  int result = get(); // [*]
}

と等価です。[*]の時点でVAL1 != result なのは明らか。

えの 2006/05/07(日) 12:29:06
ご回答、ありがとうございます!
でも、ちょっとよく解かりません・・
勉強不足でごめんなさい。

mainでは、VAL1を定義していないのですけど、
ヘッダに定義を書くこと自体がいけないんでしょうか?

構成はこんな感じです。


--- NS_A.h
static int VAL1;


--- NS_A.cpp
int set(int val) { VAL1 = val; }
int get() { return VAL1; }
void C_A::method() { cout << get() << endl; }

--- main.cpp
int main() {
  set(123);
  C_Aのインスタンス.method(); // 0が出力される
}

クラス全員が参照できる値は、
もっと上の階層?でセットするものなんでしょうか・・?
(こんがらがってきた。。)

シャノン 2006/05/07(日) 14:08:19
> クラス全員が参照できる値は、
> もっと上の階層?でセットするものなんでしょうか・・?

クラスのすべてのインスタンスから参照できる変数は、クラスの静的メンバ変数にしま
す。
複数のファイルから参照できる大域変数にするならば、static をつけてはいけません。

えの 2006/05/07(日) 15:33:48
ありがとうございます!
そうですね。静的なのに他のファイルから見れるのって、変ですね。

それで、staticを取ってみたんですけど、
「複数回定義されているシンボルが見つかりました」
というエラーが出てしまいました。

調べてみると、「pragmaとか、ifndefを使え」と
書いてあったのですが、やっぱり同じエラーが出てしまいます。

書き換えた内容はこのゆな感じです。

--- NS_A.h ---
#ifndef NS_A_H
#define NS_A_H
#pragma once

namespase ns_a {
  int VAL1;
}

#endif

--- NS_A.cpp ---
#include"NS_A.h"
using namespace ns_a;

namespase ns_a {
  int set(int val) { VAL1 = val; }
  int get() { return VAL1; }
  void C_A::method() { cout << get() << endl; }
}

--- main.cpp ---
#include"NS_A.h"
using namespace ns_a;

int main() {
  set(123);
  C_Aのインスタンス.method(); // 0が出力される
}

そもそも、namespaceの考え方が間違ってるんでしょうか・・。

επιστημη 2006/05/07(日) 17:32:02
繰り返しますが、「ヘッダに定義をかいてはいけません。」

RAPT 2006/05/07(日) 19:03:15
extern

えの 2006/05/07(日) 22:57:00
ヘッダに書いちゃダメなんですね・・。
皆から参照される情報は、どこに書いたらいいんでしょうか・・。

RAPTさん!Externと書いたら、値が見れました。ありがとうございます!
でも、名前空間の中に定義されているクラスをexternすると、
「識別子がクラスでも名前空間でもありません」
と出てしまいます。
自分と同じ型の大域クラス配列を参照するメソッドは、
定義できないんでしょうか?

επιστημη 2006/05/07(日) 22:59:59
> 自分と同じ型の大域クラス配列を参照するメソッドは、
> 定義できないんでしょうか?

なにをお訊きになりたいのかサッパリわかりません。
# externと何の関係が?

しっぽ 2006/05/07(日) 23:59:08
折角 ns_a::set(), ns_a::get() を使っているのですから、わざわざ extern を
使って VAL1 を晒すのはどうかと。「ヘッダに定義をかいてはいけません。」に
素直にしたがえば良いかと思います。

--- NS_A.h
#ifndef NS_A_H
#define NS_A_H

namespace ns_a
{
  void set(int val);
  int get();
  struct C_A{ static void method(); };
};

#endif

--- NS_A.cpp
#include"NS_A.h"
#include <iostream>

namespace ns_a {
  int VAL1;
  void set(int val) { VAL1 = val; }
  int get() { return VAL1; }
  void C_A::method() { std::cout << get() << std::endl; }
}

--- main.cpp
#include"NS_A.h"

int main()
{
  ns_a::set(123);
  ns_a::C_A::method();
  return 0;
}

えの 2006/05/08(月) 01:33:22
[[解決]]
しっぽさん、επιστημηさん、ご回答ありがとうございます!
メイン以外の所で、変数を確保したくなかったので、
結局、externを使ってしまいましたが、ちゃんと動作しました!

> 自分と同じ型の大域クラス配列を参照するメソッドは、
> 定義できないんでしょうか?

と言うのはですね、
メインで確保されたクラスAの配列に、
クラスAのメンバ関数で追加を行いたかったんです。

プログラムにすると、こんな感じです。

--- NS_A.h
extern C_A g_ClassA[]; // メインで確保される(エラーになる)

namespace ns_A {

  class C_A {
  public:
    void 登録() {
      // g_ClassAの空き番号を見つけて、新規登録する
    }
  };

}

このままだと、最初の一行でエラーなんですけど、
NS_A.cppの方で書くと、ちゃんと動きました。
嬉しいです。

どうも、ありがとうございました!!

Ban 2006/05/08(月) 02:52:52
> メイン以外の所で、変数を確保したくなかったので、

まずこの理由が理解できません。何の意味があるのでしょう。
# もしかして大域オブジェクトの初期化順とかを考慮されてのことですか。


以下は多分(説明が一部解読できなかったので憶測交じり)、
言語的にはできるけど、やり方が悪いのでエラーになっているだけかと。

> でも、名前空間の中に定義されているクラスをexternすると、
> 「識別子がクラスでも名前空間でもありません」

名前空間の中でやらないと駄目なはずですが、そうなってましたか。

> このままだと、最初の一行でエラーなんですけど、
> NS_A.cppの方で書くと、ちゃんと動きました。

エラーが出るなら、エラー内容を書きましょう。
# 多分、メイン内で宣言がぶつかってると思いますが、宣言/定義の区別はできてます
か。


そして本質的なこと。

>      // g_ClassAの空き番号を見つけて、新規登録する

このあたりの説明を読む限り、C言語でもあるまいし、g_ClassAはグローバルになどせず
に、
C_A のスタティックメンバにするなり、せめて無名名前空間に入れるなりを推奨します。
どこからでも登録内容が弄れるようではカプセル化のカの字もありません。
なんのためのクラスなのでしょうか。

えの 2006/05/08(月) 19:27:24
[[解決]]
Banさん、ご説明ありがとうございます!

ほんと、勉強不足です・・。
頭痛いですよね。。すみません。。

メイン以外の所で、変数を確保したくなかったのは、
確保した事を忘れそうなので、メインに全部お願いしようと思ったんです。
いけませんよね、やっぱり・・。

「このままだと、最初の一行でエラーなんですけど」・・と言うのは、
> 「識別子がクラスでも名前空間でもありません」
の部分に掛けているつもりだったんです。
「前述の」とかつけないと、ダメですね。ごめんなさい。

宣言/定義の区別は、さっき知りました。

やっぱり、CとC++は別物なんですね。
勘違いとか思い込みばっかりで、情けないです。
もっと勉強しようと思います。

Banさん、ありがとうございました!

επιστημη 2006/05/08(月) 20:54:48
> やっぱり、CとC++は別物なんですね。

双方のナニを比較してのことですか?

えの 2006/05/08(月) 22:36:30
うーん、はっきりとは解かりません・・。けど、
Banさんのおっしゃる、「C言語でもあるまいし〜」
から続く言葉を聞いて、そう思ったんです。

まず何より、この早合点から直さないといけませんね。。

古いスレッドには追加発言できません。