简体   繁体   English

是否有可能每个类只传递一次变量但不能使它变为静态变量?

[英]Is it possible to pass a variable only once per class but not make it static?

This question is (loosely) related to something I asked yesterday here . 这个问题(松散地)与我昨天在这里提出的问题有关。

I have just refactored a container class ( Ecosystem ), which contains pointers to Individual s: 我刚刚重构了一个容器类( Ecosystem ),它包含指向Individual s的指针:

class Ecosystem
{
    // This is an interface class providing access
    // to functions in Individual without exposing
    // the Individual class.
    // It performs global operations on the entire ecosystem
    // (like sorting individuals based on certain criteria)
    // but is also capable of invoking functions from the
    // Individual class.
    // It also holds the global configuration for this ecosystem.
    private:
        Config config;
        std::map<int, std::shared_ptr<Individual> > individuals;
    public:
        Ecosystem() {};
        void sort_individuals();
        void func1(int _individual_id)
        {
            individuals[_individual_id]->func1(config);
        }

        void func2(int _individual_id)
        {
            individuals[_individual_id]->func2(config);
        }
        // etc...
};

class Individual
{
    private:

    public:
        Individual() {};
        void func1(const Config& _config)
        {
            // Operations using _config.param_1, _config.param_2, ... 
        }

        void func2(const Config& _config)
        {
            // Operations using _config.param_n, _config.param_m, ... 
        }
        // etc...
}

I am now in the situation where I have to pass config with practically every function call to Individual objects. 我现在处于这样的情况,我必须通过config几乎每个函数调用Individual对象。 Initially I thought, well, I'll just make a static Config config; 最初我想,好吧,我只是制作一个static Config config; inside Individual , but soon I realised that I need to be able to create multiple coexisting ecosystems with different configurations. Individual内部,但很快我意识到我需要能够创建具有不同配置的多个共存生态系统。

If I understand the implications of static correctly, if I have static Config config; 如果我正确理解static的含义,如果我有static Config config; inside Individual which is assigned the value of config from Ecosystem , it will get overwritten each time I create a new ecosystem. 在从Ecosystem分配config值的Individual ,每次创建新生态系统时都会被覆盖。

My question is: is there a way to pass config once to the Individual class without making it static in order to avoid passing it as an argument with each function? 我的问题是:有没有办法将config 一次传递给Individual类而不使其为静态 ,以避免将其作为参数传递给每个函数?

I have considered having Config* config in both Ecosystem and Individual , but this means that every individual will have a pointer to the config object, which seems clunky. 我已经考虑过在EcosystemIndividual中都有Config* config ,但这意味着每个人都会有一个指向配置对象的指针,这看起来很笨拙。 What I need is the equivalent of a static member whose scope is aware of the container hierarchy, if that makes any sense. 我需要的是等效的静态成员,其范围可以识别容器层次结构,如果这有意义的话。

Once created, a Config object will not be altered, so it's possible to have a const Config config; 一旦创建,Config对象将不会被更改,因此可以有一个const Config config; .

Thank you in advance for any suggestions! 提前感谢您的任何建议!


EDIT 1 编辑1

Thank you everyone for your responses. 谢谢大家的回复。 Matthew Kraus had a point that I didn't provide the right constructor of my Ecosystem class in the original question, which is, of course, an oversight on my side. 马修克劳斯有一个观点,我在原始问题中没有提供我的生态系统类的正确构造函数,当然,这是对我的疏忽。 I see now that it might have been relevant, but I was focusing more on illustrating what kind of variable access I'm after rather than what my classes look like, so I presented a simple example. 我现在看到它可能是相关的,但我更专注于说明我所追求的变量访问类型而不是我的类的外观,所以我提出了一个简单的例子。 Apologies if I brought in confusion to the discussion with my extra comments! 如果我用额外的评论引起讨论的混乱,我会道歉!

The solutions proposed here are all very good, but I can see from the answers that C++ doesn't have any means of defining such class-level variables without defining them as static . 这里提出的解决方案都非常好,但我可以从答案中看出,C ++没有任何方法来定义这样的类级变量而不将它们定义为static What I need is a class-level variable whose scope is limited to its container. 我需要的是一个类级变量,其范围仅限于其容器。 This is just an illustration of my idea, it will not compile (and I am certain by now that this cannot be done in C++): 这只是我的想法的一个例子,它不会编译(我现在肯定这不能在C ++中完成):

class Ecosystem
{
    // This is an interface class providing access
    // to functions in Individual without exposing
    // the Individual class.
    // It performs global operations on the entire ecosystem
    // (like sorting individuals based on certain criteria)
    // but is also capable of invoking functions from the
    // Individual class.
    // It also holds the global configuration for this ecosystem.

    private:

        Config config;
        std::map<int, std::shared_ptr<Individual> > individuals;

    public:
        Ecosystem(const std::string& _file_name)
        {
            Config cfg(_file_name);

            config = cfg;

            // This is done before any Individual objects are created.
            Individual::set_config(cfg);

            for (int i = 1; i <= 10; ++i)
            {
                individuals[i] = std::make_shared<Individual>();
            }
        };

        void sort_individuals();

        void func1(int _individual_id)
        {
            individuals[_individual_id]->func1();
        }

        void func2(int _individual_id)
        {
            individuals[_individual_id]->func2();
        }
        // etc...
};

class Individual
{
    private:
        // v--- No such thing!
        scoped static Config config;

    public:
        Individual() {};

        void func1()
        {
            // Operations using config.param_1, config.param_2, ... 
        }

        void func2()
        {
            // Operations using config.param_n, config.param_m, ...
        }

        // v--- No such thing!
        scoped static void set_config(const Config& _config)
        {
            config = _config;
        }
        // etc...
}

int main(int argc, char* argv[])
{
    Ecosystem ecosystem1("config_file1.txt");

    Ecosystem ecosystem2("config_file2.txt");

    // Conduct experiments with the two ecosytems.
    // Note that when ecosystem2 is created, the line
    //
    // Individual::set_config(cfg);
    //
    // should *not* overwrite the class-level variable
    // config set when ecosystem1 was created. 
    // Instead, it should create a
    // class-level variable but limited to the scope of ecosystem 2.

    // This operates on Individual 1 in ecosystem 1
    // with the parameters set in config_file1.txt
    ecosystem1->func1(1);

    // This operates on Individual 1 in ecosystem 2
    // with the parameters set in config_file2.txt
    ecosystem2->func1(1);

    return 0;
}

I will go with the suggestion to pass a pointer to the config to each individual at construction time. 我将建议在构造时将指针传递给每个人。 This will waste space but should be the most easily maintainable solution. 这会浪费空间,但应该是最容易维护的解决方案。

Again, thank you all for your input, and apologies if the question was confusing. 再次感谢大家的意见,如果问题令人困惑,请道歉。

If I understand you correctly, passing a config to a constructor of an Individual should do: 如果我理解正确,将配置传递给Individual的构造函数应该:

class Individual {
    const Config& config_;
public:
    Individual(const Config& c) : config_(c) {}
    void func1() {
        // use config_
    }
};

You still have to pass Config to every individual constructor, but this is a single point, rather than however many functions Individual will have. 您仍然必须将Config传递给每个单独的构造函数,但这只是一个单点,而不是Individual将拥有的许多函数。 Moreover, you could encapsulate the creation of an Individual into a factory method if you felt like the creation was too cumbersome. 此外,如果您觉得创建过于繁琐,您可以将Individual的创建封装到工厂方法中。

If Individual objects shouldn't contain a configuration, then there is always 如果Individual对象不应包含配置,则始终存在

class ConfiguredIndividualReference
{
private:
    std::shared_ptr<Individal> ptr; // or just Individual* depending on how you use this
    const Config &config;
public:
    // ...
    void func1()
    {
        ptr->func1(config);
    }
    // ...
};

Furthermore, you can make a member function to automatically generate this: 此外,您可以使成员函数自动生成:

ConfiguredIndividualReference fetch(int id) const {
    return ConfiguredIndividualReference(individuals[id], config);
}

and always use this to fetch the individuals rather than directly accessing the map. 并始终使用此来获取个人而不是直接访问地图。

A few answers trickled in while I was working on a solution, but I thought I'd post my answer anyway. 我正在研究一个解决方案时,有几个答案在流淌,但我想我还是会发布我的答案。 Here is a complete solution to see everything in context: 这是一个完整的解决方案,可以查看上下文中的所

#include <iostream>
#include <string>
#include <map>

class Config
{
public:
    explicit Config(std::string name) : name(name) {}

    std::string getName() const { return name; }

private:
    std::string name;
};

class Individual
{
public:
    // using 'explicit' ensures that an Individual is created with a Config
    explicit Individual(const Config& config) :
        config(config) {}

    std::string getConfigName() const { return config.getName(); }

    void func1() {}
    void func2() {}

private:
    // a "const reference" is read-only
    const Config& config;
};

// notice that the dependency on Config is removed from Ecosystem
class Ecosystem
{
    private:
        // std::map<int, std::shared_ptr<Individual> > individuals;
        // I compiled using Individual*, so be aware
        // of the implications of using a naked ptr vs shared_ptr
        std::map<int, Individual*> individuals

    public:
        Ecosystem() {};

        void sort_individuals();
        void func1(int _individual_id)
        {
            // no dependency on Config
            individuals[_individual_id]->func1();
        }

        void func2(int _individual_id)
        {
            individuals[_individual_id]->func2();
        }
};

int main()
{
    using namespace std;

    Config globalConfig("global configuration");
    Config localConfig("local configuration");

    // the Config object passed to an Individual must
    // last the lifetime of the Individual
    Individual individualA(globalConfig);
    Individual individualB(localConfig);

    // the following prints "global configuration"
    cout << "individualA config is: " << individualA.getConfigName() << endl;

    // the following prints "local configuration"
    cout << "individualB config is: " << individualB.getConfigName() << endl;

    return 0;
}

As noted in the code, be sure that the Config object out-lives any object that depends on it. 如代码中所述,请确保Config对象超出依赖于它的任何对象。 Sharing object like this--even if you are making a const reference--removes the physical dependency, but it doesn't remove the logical dependency. 像这样共享对象 - 即使您正在进行const引用 - 删除了物理依赖关系,但它不会删除逻辑依赖关系。 In other words, the C++ compiler can compile the Ecosystem class without knowledge of a Config . 换句话说,C ++编译器可以在不知道Config情况下编译Ecosystem类。 However, the Ecosystem depends on Individual s, which in turn depend on Config s, so be forewarned. 然而, Ecosystem依赖于Individual ,而Individual依赖于Config ,因此需要预先警告。 My point here is that a const reference is the vanilla C++ way to do this, but it might be worth considering std::shared_ptr if managing the lifetime of Config objects is anything but trivial. 我的观点是const引用是vanilla C ++的方法,但是如果管理Config对象的生命周期是微不足道的话,可能值得考虑std::shared_ptr

Edit 编辑

I didn't actually use shared_ptr when I compiled this; 我在编译时没有实际使用shared_ptr ; I used Individual* in the map. 我在地图中使用了Individual* Just be aware of the implications of using one vs. the other. 请注意使用一个与另一个的含义。

I'm posting a second answer to meet the new requirement of only having one Config per Ecosystem instead of one per Individual : 我张贴第二个答案,以满足只有一新的要求Config每个Ecosystem ,而不是每一Individual

#include <iostream>
#include <string>
#include <map>

class Config
{
public:
    explicit Config(std::string name) : name(name) {}

    std::string getName() const { return name; }

private:
    std::string name;
};

class Individual
{
public:
    Individual() {}

    void func1(const Config& config) const
    {
        std::cout << config.getName() << std::endl;
    }

    // you might remove the dependency of Config and
    // just pass only the config parameters needed.
    void func2(Type1 configParam1, Type2 configParam2) {}
};

// notice that the dependency on Config is removed from Ecosystem
class Ecosystem
{
public:
    explicit Ecosystem(Config& config) : config(config) {}

    void sort_individuals();
    void func1(int _individual_id)
    {
        individuals[_individual_id]->func1(config);
    }

private:
    // std::map<int, std::shared_ptr<Individual> > individuals;

    // I compiled using Individual*, so be aware
    // of the implications of using a naked ptr vs shared_ptr
    std::map<int, Individual*> individuals;

    const Config& config;    
};

int main()
{
    using namespace std;

    Config globalConfig("global configuration");

    // Config objects don't need to be created in main();
    // you could create them on the heap, too:
    Config* localConfig = new Config("local configuration");

    Ecosystem ecosystemA(globalConfig);
    Ecosystem ecosystemB(*localConfig);

    // TODO: add Individuals to each Ecosystem

    // clean up objects on heap
    delete localConfig;

    return 0;
}

I feel like we already answered the OP's original question of "is there a way to pass config once to the Individual class without making it static in order to avoid passing it as an argument with each function" . 我觉得我们已经回答了OP的原始问题“是否有一种方法可以将配置文件一次传递给Individual类,而不会将其设置为静态,以避免将其作为每个函数的参数传递” This example gives a few more options. 此示例提供了更多选项。

The addition here is creating the Config object on the heap. 这里的添加是在堆上创建Config对象。 This makes the caller of Ecosystem(Config& config) responsible for managing the Config object. 这使得Ecosystem(Config& config)的调用者负责管理Config对象。 Again, this doesn't have to happen in main() , but you might modify my example to use smart pointers to automatically manage the lifetime of Config objects. 同样,这不一定发生在main() ,但您可以修改我的示例以使用智能指针自动管理Config对象的生命周期。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM