Mr.Mountain
Mr.Mountain

Reputation: 883

C++ container classes

currently I switch from Java to C++ and this is giving me a hard time (but lots of new experience ^^). I'm writing some data-transfer-objects which contain configurations for my program. I've written some classes and now I want to have a class which behaves like a container.

Here's a header for the container:

class MyContainer{

public:
MyContainer();
virtual ~MyContainer();

Config getConfig(TypeEnum type) {
    switch (type) {
    case ATYPE:
        return config_a;
    case BTYPE:
        return config_b;
    default:
        break;
    }

}

ConfigA config_a;
    ConfigB config_b;
};

The configs have some data in it and are derived from another config-file.

And here is the C++-Source:

 MyContainer::MyContainer(){
  ConfigA config_a(int c = 1);
  ConfigB config_b(double b = 2.1);
  this->config_a = config_a;
  this->config_b = config_b;
}

There are several problems I think. But the main question for me is: How can I get those configs in this container to share it to other modules of my program? I have tried to make config_a to a pointer but I always get error-messages that these types won't match.

 this->config_a = &config_a; //If ConfigA config_a; was ConfigA *config_a; instead

If you have another minute for me then please tell me if the getConfig-Method could work like this.

And if there's another topic for this then please share. Thanks.

Upvotes: 0

Views: 629

Answers (2)

Spook
Spook

Reputation: 25927

  1. If you write ConfigA configA in your header, the configA is automatically allocated during the allocation of the container class. So you don't have to initialize it like following:

    ConfigA config_a(1);
    this->config_a = config_a;
    

    Instead, you can just do the following:

    this->config_a->value = 1;
    
  2. There is no need to write:

    ConfigA config_a(int c = 1);
    

    In short words, the mentioned int c = 1 is an operation, which:

    • allocates space on heap for temporary variable c (this may be done when entering the method as well)
    • assigns value to it, which has side effect of returning the right-hand-side value
    • the returned right-hand-side value is applied to the ConfigA constructor.

    To understand this, try the following:

    int a, b, c;
    c = (a = 2) + (b = 8);
    printf("a = %d, b = %d, c = %d\n", a, b, c);
    
  3. If you want to pass the configs to another modules, you can choose one of the following solutions:

    a) Accept the configs as references (the config classes have to derive from the same base class):

    ConfigA & configA = dynamic_cast<ConfigA &>(container.get_config(ATYPE));
    

    In this case, container shall return the configs in the following way:

    return this->configA;
    

    But the header shall be modified:

    Config & getConfig(TypeEnum type) { (...)
    

    b) Accept the configs as pointers (same as above)

    ConfigA * configA = dynamic_cast<ConfigA *>(container.get_config(ATYPE));
    

    In this case, container shall return the configs in the following way:

    return &(this->configA);
    

Finally, this is the way I would do it:

class ConfigA
{
private:
    int i;

public:
    int GetI() const { return i; }
    void SetI(int newI) { i = newI; }
};

class ConfigB
{
private:
    float f;

public:
    float GetF() const { return f; }
    void SetF(float newF) { f = newF; }
};

class Config
{
private:
    ConfigA configA;
    ConfigB configB;

public:
    ConfigA & GetConfigA() { return configA; }
    ConfigB & GetConfigB() { return configB; }
};

class AppContext
{
private:
    Config config;

public:
    Config & GetConfig() { return config; }
};

// Somewhere in the code

Config & config = context.GetConfig();
ConfigA & configA = config.GetConfigA();
configA.SetI(44);

The const-correctness is also worth mentioning, but we'll leave it as an exercise for the OP ;)

Upvotes: 1

StackHeapCollision
StackHeapCollision

Reputation: 1813

Your code:

ConfigA config_a(int c = 1);

Means a function prototype that returns ConfigA and takes an int with a default value of 1. But if you seperate the int declaration from the expression, it will change to something else:

int c;
ConfigA config_a(c = 1); //  This is construction of a ConfigA object

I would write the constructor like this:

MyContainer::MyContainer(int c = 1, double d = 2.1)
 :    config_a (c), config_b (d)  //  Construct the members in the intialization line
{
}

If you want to share a pointer to these members, return a pointer like this:

ConfigA * MyContainer::GetConfigA(){return &config_a;}
ConfigB * MyContainer::GetConfigB(){return &config_b;}

But if you want to work with pointer members:

 MyContainer::MyContainer(int c = 1, double d = 2.1)
 :    pConfig_a (new ConfigA (c)), pConfig_b (new ConfigB (d))
{
}


ConfigA * MyContainer::GetConfigA(){return pConfig_a;}
ConfigB * MyContainer::GetConfigB(){return pConfig_b;}


 MyContainer::~MyContainer()
{
    delete pConfig_a;
    delete pConfig_b;
}

Upvotes: 1

Related Questions