如何在 C++ 中跨 class 个文件使用变量?

How to use variables across class files in C++?

我需要使用我在一个 class 中分配给另一个的变量。例如我有这段代码。这是 CharacterCreation.h 后面是 CharacterCreation.cpp

#ifndef CHARACTERCREATION_H
#define CHARACTERCREATION_H

class CharacterCreation
{
public:
    CharacterCreation();
};

#endif
#ifndef CHARACTERCREATION_H
#define CHARACTERCREATION_H

class CharacterCreation
{
public:
protected:
};

#endif

这里是CharacterCreation.cpp

#include <iostream>
#include "CharacterCreation.h"
#include <string>

CharacterCreation::CharacterCreation()
{
int warrior, mage, rogue, priest;
int class1;
int classID;

std::cout   << "Choose a class:\n"
            << "[1] Warrior\n"
            << "[2] Mage\n"
            << "[3] Rogue\n"
            << "[4] Priest\n" << std::endl;

std::cin    >> class1;

switch(class1)
{
    case 1:
        classID=1;
        std::cout   << "Learned Sword Combat!\n\n";
        break;


    case 2:
        classID=2;
        std::cout   << "Learned the Arcane Arts!\n\n";
        break;


    case 3:
        classID=3;
        std::cout   << "Learned the Art of Assasination!\n\n";
        break;

    case 4:
        classID=4;
        std::cout   << "Learned the Art of the Divine!\n\n"; 
        break;
}

}

我需要在 main.cpp

中使用 class1 变量
#include <iostream>
#include <string>
#include "CharacterCreation.h"
int main()
{
    switch(class1)
        {
        case 1: std::cout << "You chose warrior\n";

        case 2: std::cout << "You chose mage\n";

        case 3: std::cout << "You chose rogue\n";

        case 4: std::cout << "You chose priest\n";
        }
}

这段代码只是我需要的一个示例,所以不用担心它不起作用。我只需要将我的变量从 CharacterCreation.cpp 转移到 main.cpp 的方法,它们等于我在 CharacterCreation.cpp 中教我设置的值。

我几乎是 C++ 的新手,所以如果您可以 ELIF 您教授的任何方法,那就太好了。

与评论者所说的相反 "overuse of OOP" - 我认为情况并非如此。

事实上,代码具有编程新手的所有特征。例证:

  • 使用构造函数input/output
  • 重复代码(一般)
  • repeated switch on "class id" (specificly) <-- this 是缺少的地方面向对象显示 IMO。

    每当你重复打开某种类型识别时,你真的想要 Polymorphic Behaviour。您可以为一些具有不同行为的 classes 建模:

    class Character {
      public:
        virtual std::string name()  const = 0;
        virtual void acquireSkill() const = 0;
        virtual ~Character();
    };
    
    class Warrior : public Character { 
        virtual std::string name()  const override;
        virtual void acquireSkill() const override;
    };
    
    class Mage    : public Character { 
        virtual std::string name()  const override;
        virtual void acquireSkill() const override;
    };
    
    class Rogue   : public Character { 
        virtual std::string name()  const override;
        virtual void acquireSkill() const override;
    };
    
    class Priest  : public Character { 
        virtual std::string name()  const override;
        virtual void acquireSkill() const override;
    };
    

    现在您可以按如下方式使用它:

    CharacterCreation factory;
    CharacterPtr character = factory.createCharacter(choice);
    
    std::cout << character->name() << "\n";
    character->acquireSkill();
    
  • 输入需要验证。良好的输入错误处理是 teh hardz 使用 C++ 的标准库 iostreams 设施。有关一些信息,请参见下面的演示 想法(虽然现在超出了我的解释范围)。

  • The Creation class 可能旨在作为一种工厂。那么,就这样吧:

    using CharacterPtr = std::shared_ptr<Character>;
    
    class CharacterCreation {
    public:
        enum class Type { none, warrior, mage, rogue, priest };
        CharacterPtr createCharacter(Type type);
    };
    

    请注意 实现 of createCharacter still 不做输入选择!

    CharacterPtr CharacterCreation::createCharacter(Type type) {
        switch (type) {
            case Type::warrior: return std::make_shared<Warrior>();
            case Type::mage:    return std::make_shared<Mage>();
            case Type::rogue:   return std::make_shared<Rogue>();
            case Type::priest:  return std::make_shared<Priest>();
            case Type::none: // fall through
                break;
        }
        throw std::range_error("Type"); // character type not implemented?
    }
    

    Note: the choice for shared_ptr was a bit arbitrary here. The point is, for polymorphic object behaviour you need to hold references the the base-type (implying you typically dynamically allocate the specific Character subclasses)

事不宜迟,单个文件中的完整示例:

Live On Coliru

#ifndef CHARACTERCREATION_H
#define CHARACTERCREATION_H

#include <memory>
#include <string>

class Character {
public:
    virtual std::string name()  const = 0;
    virtual void acquireSkill() const = 0;
    virtual ~Character();
};

class Warrior : public Character { 
    virtual std::string name()  const override;
    virtual void acquireSkill() const override;
};

class Mage    : public Character { 
    virtual std::string name()  const override;
    virtual void acquireSkill() const override;
};

class Rogue   : public Character { 
    virtual std::string name()  const override;
    virtual void acquireSkill() const override;
};

class Priest  : public Character { 
    virtual std::string name()  const override;
    virtual void acquireSkill() const override;
};

using CharacterPtr = std::shared_ptr<Character>;

class CharacterCreation {
public:
    enum class Type { none, warrior, mage, rogue, priest };
    CharacterPtr createCharacter(Type type);
};

#endif

#include <iostream>
//#include "CharacterCreation.hpp"

Character::~Character() { }

CharacterPtr CharacterCreation::createCharacter(Type type) {
    switch (type) {
        case Type::warrior: return std::make_shared<Warrior>();
        case Type::mage:    return std::make_shared<Mage>();
        case Type::rogue:   return std::make_shared<Rogue>();
        case Type::priest:  return std::make_shared<Priest>();
        case Type::none: // fall through
            break;
    }
    throw std::range_error("Type"); // character type not implemented?
}

std::string Warrior::name() const  { return "Warrior"; } 
std::string Mage::name()    const  { return "Mage";    } 
std::string Rogue::name()   const  { return "Rogue";   } 
std::string Priest::name()  const  { return "Priest";  } 

void Warrior::acquireSkill() const  { std::cout << "Learned Sword Combat!\n\n";            } 
void Mage::acquireSkill()    const  { std::cout << "Learned the Arcane Arts!\n\n";         } 
void Rogue::acquireSkill()   const  { std::cout << "Learned the Art of Assasination!\n\n"; } 
void Priest::acquireSkill()  const  { std::cout << "Learned the Art of the Divine!\n\n";   } 

//// main.cpp
#include <iostream>
#include <string>
//#include "CharacterCreation.hpp"

namespace {
    template <typename T, typename Prompt, typename Validation>
        T input(std::istream& is, Prompt prompt, Validation valid)
        {
            T result;
            while (prompt(), !(is >> result) || !valid(result)) {
                if (!is && is.eof())
                    throw std::runtime_error("End of file reading input");

                is.clear();
                is.ignore(10u << 20, '\n');
            }

            return result;
        }
}

int main() {
    auto choice = static_cast<CharacterCreation::Type>(
            input<int>(
                std::cin, 
                [] { std::cout << "Choose a character:\n"
                        << "[1] Warrior\n"
                        << "[2] Mage\n"
                        << "[3] Rogue\n"
                        << "[4] Priest\n"; },
                [](int choice) { 
                    std::cout << "Validation(" << choice << ")\n";
                    return choice>=1 && choice <=4; 
                }
            )
        );

    CharacterCreation factory;
    CharacterPtr character = factory.createCharacter(choice);

    std::cout << character->name() << "\n";
    character->acquireSkill();
}

打印(输入“4”时):

Choose a character:
[1] Warrior
[2] Mage
[3] Rogue
[4] Priest
Validation(4)
Priest
Learned the Art of the Divine!
CharacterCreation::CharacterCreation()
{
int warrior, mage, rogue, priest;
int class1;

构造函数与任何其他类型的函数非常相似。就像常规函数一样,您可以声明在函数外部无法访问的局部变量。这就是你在这里所做的。

这是同一问题的简化示例:

#include <iostream>

void foo() {
  int aNumber;
  std::cout << "Enter a number! ";
  std::cin >> aNumber;
}

int main() {
  std::cout << "You entered the number " << aNumber << "\n";
}

计算机程序可能非常大而且非常复杂,这使得它们很难确保它们在添加新功能和修复错误时继续工作。因此,要避免复杂性并确保程序尽可能易于处理,最关键的技术之一就是尽可能将程序的每个部分与程序的其他部分隔离开来,并严格控制各部分如何工作程序相互交互。

语言以不同的方式支持这一点。其中一种方法是 变量作用域 的概念,或者是允许与变量交互的程序区域。特别是 C++ 限制了函数中变量的范围,这样函数之外的任何东西都无法访问它。

当您确实想让程序的不同部分交互时,有很多很多方法可以做到这一点,尝试列出它们没有多大意义。每种不同的方法都适用于不同的编程目标,所以这真的取决于你在做什么。

我不确切地知道您的目标是什么,但是拥有 CharacterCreation class 并在构造函数中接受用户输入似乎是一个可疑的设计,而且它可能使事情过于复杂。也就是说,这里有一种方法可以使程序根据明显的意图运行:

// CharacterCreation.h
#ifndef CHARACTERCREATION_H
#define CHARACTERCREATION_H

class CharacterCreation {
  int classID;

public:
  CharacterCreation();
  int getClass();
};

#endif // CHARACTERCREATION_H

// CharacterCreation.cpp

#include "CharacterCreation.h"
#include <cstdlib>  // abort()
#include <iostream> // cout, cin, cerr

CharacterCreation::CharacterCreation() {
  std::cout << "Choose a class:\n[1] Warrior\n[2] Mage\n"
               "[3] Rogue\n[4] Priest\n\n";

  std::cin >> classID;
  if (std::cin.fail()) {
    std::cerr << "Error: failed to get user input.\n";
    throw std::runtime_error("input failed");
  }

  if (classID < 1 || 4 < classID) {
    std::cerr << "Error: invalid user input.\n";
    throw std::runtime_error("invalid selection");
  }

  switch (classID) {
  case 1:
    std::cout << "Learned Sword Combat!\n\n";
    break;
  case 2:
    std::cout << "Learned the Arcane Arts!\n\n";
    break;
  case 3:
    std::cout << "Learned the Art of Assasination!\n\n";
    break;
  case 4:
    std::cout << "Learned the Art of the Divine!\n\n";
    break;
  default:
    abort();
  }
}

int CharacterCreation::getClass() { return classID; }

// main.cpp

#include "CharacterCreation.h"
#include <cstdlib>  // abort()
#include <iostream> // cout, cin, cerr

int main() {
  CharacterCreation cc;

  switch (cc.getClass()) {
  case 1:
    std::cout << "You chose warrior\n";
    break;

  case 2:
    std::cout << "You chose mage\n";
    break;

  case 3:
    std::cout << "You chose rogue\n";
    break;

  case 4:
    std::cout << "You chose priest\n";
    break;
  default:
    abort();
  }
}

如果我只想编写一个具有相同输出的程序,我可能会这样写:

// main.cpp

#include <iostream>
#include <array>

struct CharacterClass {
  char const *name;
  char const *output;
};

int main() {
  std::array<CharacterClass, 4> classes = {
      {{"Warrior", "Learned Sword Combat!\n\nYou chose warrior"},
       {"Mage", "Learned the Arcane Arts!\n\nYou chose mage"},
       {"Rogue", "Learned the Art of Assasination!\n\nYou chose rogue"},
       {"Priest", "Learned the Art of the Divine!\n\nYou chose priest"}}};

  std::cout << "Choose a class:\n";
  for (int i = 0; i < classes.size(); ++i) {
    std::cout << "[" << i + 1 << "] " << classes[i].name << "\n";
  }
  int classID;
  std::cin >> classID;
  if (std::cin.fail()) {
    std::cerr << "Error: failed to get user input.\n";
    throw std::runtime_error("input failed");
  }

  if (classID < 1 || classes.size() < classID) {
    std::cerr << "Error: invalid user input.\n";
    throw std::runtime_error("invalid selection");
  }

  std::cout << classes[classID - 1].output << '\n';
}