从另一个类模块引用在 main 中创建的类的实例

Posted

技术标签:

【中文标题】从另一个类模块引用在 main 中创建的类的实例【英文标题】:Referencing an instance of a class created in main from another class module 【发布时间】:2021-01-25 21:41:57 【问题描述】:

我正在研究一种需要两副牌的扑克变体。出于统计原因,重要的是在洗牌两副牌时使用通用的随机数生成器。为了进行测试,我有一个小主程序,我在其中创建了一个随机数生成器的实例和两个套牌实例。我用语句“extern classRNG Random”引用了 classDeck 中的实例“Random”。

一切编译正常,但我收到链接器错误“Deck.obj : error LNK2001: unresolved external symbol "class classRNG Random" (?Random@@3VclassRNG@@A)。”如果我在 classDeck 而不是在 main 中创建实例 Random ,一切都可以编译和链接,但当然这将为每个甲板创建一个单独的随机数生成器。

我显然忽略了一些东西。

要处理很多代码,但我认为错误是由 Deck.cpp 中靠近 Deck.cpp 末尾的函数“Shuffle”造成的。那就是我有“extern classRNG Random”的声明。注释掉“RawDeck[i].SortParam = Random.RNGGet()”行可以消除错误。

这是 RNG.h 的代码:

#ifndef RNG_H 
#define RNG_H 

class classRNG

private:
    static unsigned long long Seed;

public:   
    classRNG();   // Default constructor;
    classRNG(unsigned long long ullSeed); // Constructor with initial seed
    unsigned long long RNGGet();  //Get a random number
;
#endif

RNG.cpp:

#include <iostream>
#include <vector>
#include <string>
#include <math.h>
#include <stdbool.h>
#include "RNG.h"

using namespace std;

const unsigned long long T1 = 60LL, T2 = 59LL;

classRNG::classRNG()   // Default constructor

int i;

Seed = 3;

for (i=0; i<7*T1; i++)      //Step thru some numbers to stabilize

    RNGGet();

;

classRNG::classRNG(unsigned long long ullSeed) // Constructor with initial seed

const unsigned long long Mask1 = powl(2LL,T1);
const unsigned long long Mask = Mask1 - 2LL;

int i;

Seed = ullSeed % Mask;
for (i=0; i<7*T1; i++)      //Step over trivial numbers

    RNGGet();

;

// This method is used to obtain a random number.  It returns an unsigned Long Long.
unsigned long long classRNG::RNGGet()

const long long Test1 = powl(2LL, T1-1LL);
const long long Test2 = powl(2LL, T2-1LL);
const unsigned long long Mask1 = powl(2LL,T1);
const unsigned long long Mask = Mask1 - 2LL;
const unsigned long long Taps = Test1 | Test2;
const unsigned long long Scram = 0x693474786E579bd % Mask;

unsigned long long Test;

Test = Seed & Taps;
Seed = (Seed << 1) & Mask;
if (Test == Test1 | Test == Test2)
    Seed = Seed | 1LL;

return Seed * Scram;
;

unsigned long long classRNG::Seed = 3;

甲板.h:

#ifndef DECK_H
#define DECK_H

#include "struct_defs.h"

class classDeck

private:
    static int NextCard;    // Pointer to next card in the deck 
    static bool Ready;  // Indicates that deck is not dirty
    static structSortCard RawDeck[53];  // Stores the raw card value (1-52) with a sort parameter

    bool SortDeck(int Plow, int Phi);   // Used in Shuffle
    void SwapCards(int P1, int P2);     // Used in SortDeck

public:
    classDeck();  //Constructor;
    bool InitializeDeck();
    bool Shuffle();
    structCardDef GetCard();
    bool DeckReady ();
;
#endif

Deck.cpp:

#include <iostream>
#include <vector>
#include <string>
#include <math.h>
#include <stdbool.h>
#include "RNG.h"
#include "struct_defs.h"    // Data structures
#include "Deck.h"

using namespace std;

/* 
This type manages the card deck.  No data are directly exposed.  It provides the following methods:
- InitializeDeck.   Restores deck to unsorted state. (Probably never use this)
- Shuffle.          Returns True when complete.
- GetCard.          Returns the next card as Class classCardDef.
- DeckReady.        Returns True if the deck is shuffled and not dirty. 
*/

int classDeck::NextCard = 1;
bool classDeck::Ready = false;
structSortCard classDeck::RawDeck[] = 0;

classDeck::classDeck()  //Constructor

InitializeDeck();
;


bool classDeck::SortDeck(int Plow, int Phi)

int Pivot, P1, P2, P3, i;

Ready = false;

// Check for trivial cases
if ((Phi - Plow) < 2)   // Nothing to sort

    return true;

if (((Phi - Plow) == 2) && RawDeck[Plow].SortParam > RawDeck[Phi].SortParam)    // Only two - easy peasy

    SwapCards(Phi, Plow);
    return true;


// Set Pivot to last element
Pivot = Phi;
P3 = 0;

// Search for an element larger than the pivot, and set P1 to that
for (P1 = Plow; P1 < Pivot; P1++)

    if(RawDeck[P1].SortParam > RawDeck[Pivot].SortParam)    // Found a large number
    
        // Search from P1+1 to Pivot-1 to find an element smaller than the pivot and set P2 to that
    for (P2 = P1+1; P2 < Pivot; P2++)
        
            if(RawDeck[P2].SortParam < RawDeck[Pivot].SortParam)    // Found a small number
            
                // Swap elements at P1 & P2
                SwapCards(P1, P2);
                P3 = P1;    // Remember leftmost large card
                break;
            
        
        if (P2 >= Pivot)   // Reached the pivot without finding another small card
        
            break;
        
    


// Move Pivot to P3
if (P3 > 0) // There's at least one large card

    SwapCards(P3, Pivot);  // The Pivot is now at P3


// Split into two parts, and iterate any part with more than one element
Ready = SortDeck(Plow, P3 - 1) && SortDeck(P3 + 1, Phi);
return Ready;
;


void classDeck::SwapCards(int P1, int P2)

structSortCard Temp;

Temp.Value  = RawDeck[P1].Value;
Temp.SortParam = RawDeck[P1].SortParam;

RawDeck[P1].Value = RawDeck[P2].Value;
RawDeck[P1].SortParam = RawDeck[P2].SortParam;

RawDeck[P2].Value = Temp.Value;
RawDeck[P2].SortParam = Temp.SortParam;
;



bool classDeck::InitializeDeck()

int i;

Ready = false;
for (i = 1; i <= 52; i++)

    RawDeck[i].Value = i;

Ready = Shuffle();
return Ready;
;


bool classDeck::Shuffle()

int i;
extern classRNG Random;

Ready = false;
for (i = 1; i <= 52;i++)

    RawDeck[i].SortParam = Random.RNGGet();


// Sort the deck
Ready = SortDeck(1, 52);
return Ready;
;


structCardDef classDeck::GetCard()

structCardDef NewCard;
int Card;

Ready = false;
Card = RawDeck[NextCard++].Value;
NewCard.Card = Card % 13 + 1;
NewCard.Suit = Card / 13 + 1;
return NewCard;
;


bool classDeck::DeckReady ()

return Ready;
;

最后是 main.cpp

#include <iostream>
#include <vector>
#include <string>
#include <math.h>
#include <stdbool.h>
#include "RNG.h"
#include "struct_defs.h"
#include "Deck.h"

using namespace std;

int main()

unsigned long long X;
classRNG Random;    // Create an instance of classRNG
X = Random.RNGGet();

// Create two decks
classDeck Deck[2];

结构“structSortCard”在“struct_defs.h”中定义为:

struct structSortCard

public:
    int Value;
    unsigned long long SortParam;
;

我尝试了很多东西都无济于事。

【问题讨论】:

听起来你可能想要Deck 类中的静态成员变量,如果该类的不同实例应该共享一个底层对象。我建议调查一下,而不是试图让 extern 指向您的 main 函数中的某些内容。 【参考方案1】:

classRNG Random 是 main 的本地变量 - 它不是全局变量,因此在 classDeck::Shuffle 中的 extern 调用中不可见。使用 extern 共享变量有点奇怪 - 为什么不让 Shuffle 使用 classRNG* 参数,这样您就可以将相同的 classRNG 传递给每个实例(或者如果您可以在构造函数中传递它更喜欢)。或者,您也可以让 RNG 成为 classDeck 的静态成员,这样平台将负责构建一个在所有实例之间共享的 classRNG

【讨论】:

谢谢。这样就可以了。但是你为我发现了另一个问题。我习惯了其他语言的静态,这仅意味着该值在调用之间保持不变。我假设在 C++ 中就是这种情况,但显然在 C++ 中这也意味着它由类的所有实例共享。我在 Deck 中有三个变量需要持久化,但每个实例必须是唯一的。我将不得不重新考虑这一点。 static 在 C++ 中具有不同的含义,具体取决于它所在的位置。正如您所说,static 类成员在该类的所有实例之间共享。如果您需要一些对类的每个成员都是唯一的数据,那么这只是一个普通的成员变量。我不太确定如何在对象的生命周期之外拥有一个“持久”变量并且让该变量对该实例是唯一的。如果数据对于实例是唯一的,那么它只是一个常规实例成员,当类超出范围时,数据将被删除。【参考方案2】:

此声明

extern classRNG Random;

在全局命名空间中声明(但不定义)变量Random

这个声明同时在main中定义了变量Random

int main()

unsigned long long X;
classRNG Random;    // Create an instance of classRNG
//...

声明一个在main的块范围之外不可见的局部变量。

所以在全局命名空间中声明的变量Random 仍然未定义。

您可以将局部变量 Random 的声明(没有存储说明符 extern)移到函数 main 之前。在这种情况下,此声明将是在全局命名空间中声明的变量 Random 的定义。

另一种方法是在classDeck 类中创建classRNG 类型的(静态)数据成员。

【讨论】:

以上是关于从另一个类模块引用在 main 中创建的类的实例的主要内容,如果未能解决你的问题,请参考以下文章

在 aux 方法中创建的 QWidget 不显示/绘制

访问另一个类的 main 中定义的类的实例

QT QML - 从另一个类访问 qml 模型

在构造函数中创建的外部向量

Kivy - 在其他屏幕中创建的访问实例

为什么不能引用在Swift项目的同一目录中创建的类或结构?