0

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.

Mat
  • 202,337
  • 40
  • 393
  • 406
Mr.Mountain
  • 863
  • 8
  • 32
  • 2
    Get a good C++ book, you totally misunderstand how C++ works (which is not uncommon for java programmers) –  Jan 02 '13 at 13:15
  • (Avoid CPP when talking about C++ - CPP usually refers to the C pre-processor.) – Mat Jan 02 '13 at 13:34
  • @Mr.Mountain http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list – jogojapan Jan 02 '13 at 13:35

2 Answers2

1
  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 ;)

Spook
  • 25,318
  • 18
  • 90
  • 167
  • I'd hope there is a way to pass the configuration without using `dynamic_cast`. That should be a last resort, really. But +1 for trying to explain the basics. – jogojapan Jan 02 '13 at 13:37
  • This is why I would rather make different getters for different configs rather than return them in one generic method getting an enum as a parameter. Or better, wrap all the configs in one class with separate getters. – Spook Jan 02 '13 at 14:57
1

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;
}
StackHeapCollision
  • 1,743
  • 2
  • 12
  • 19