1

The following is directly related to this. What I would like is to be able to call and have active only one class at a time, to save memory, but also because I plan to add, later on, a GUI, so I'd be able to call the classes through a drop-down menu (for example).

I tried making composition, and this is what came out:

#include <iostream>

class Power
{
private:
    double m_x;
public:
    Power() {std::cout<<"Power\n";}
    Power(double x): m_x {x} {std::cout<<"Power("<<x<<")\n";}
    ~Power() {std::cout<<"~Power\n";}
    const double getX() const { return m_x; }
};

class Scanner
{
private:
    Power m_power;
public:
    Scanner() {std::cout<<"Scanner\n";}
    Scanner(const Power &p): m_power {p} {std::cout<<"Scanner("<<&p<<")\n";}
    void print() {std::cout<<"x="<<m_power.getX()<<'\n';}
};

class Printer
{
private:
    Power m_power;
public:
    Printer() {std::cout<<"Printer\n";}
    Printer(const Power &p): m_power {p} {std::cout<<"Printer("<<&p<<")\n";}
    void print() {std::cout<<"x="<<m_power.getX()<<'\n';}
};

class Copier // if Copier is to be used for "unification", will "public" be needed?
{
private:
    Scanner *m_s;
    Printer *m_p;
    int m_i;
public:
    Copier() {std::cout<<"Copier\n";}
    Copier(const Power &p, int i): m_i {i}
    {
        if (i)
            m_s = new Scanner(p);
        else
            m_p = new Printer(p);
        std::cout<<"Copier("<<&p<<","<<i<<")\n";
    }
    void print() { std::cout << (m_i ? m_s->getX() : m_p->getX()) << '\n'; }
};

int main(int argc, char *argv[])
{
    Scanner *s {new Scanner(Power(2.3))};
    s->print();
    Printer *p {new Printer(Power(3.14))};
    p->print();
    s->print(); // here, both *s and *p exist, both use memory
    // this comes after considering adding class Copier
    Copier *c {new Copier(Power(1.618), 0)};
    c->print();
    c = new Copier(Power(2.718), 1);
    c->print();
    return 0;
}

Ignore Copier for a bit. As it is, I can use it, and this is what comes out:

Power(2.3)
Scanner(0x7ffc80d98c10)
~Power
x=2.3
Power(3.14)
Printer(0x7ffc80d98c20)
~Power
x=3.14
x=2.3

The (major) problem now is that there are multiple objects in memory, there's *s and there's *p, as you can see x can pe printed out with both 3.14 and 2.3. If I have more than 2 classes (which I do), I could call each class and each will take up memory. That's not something I want.

How can I call only one class at a time and not have to call extra resets or deletes? I thought of adding another class for it, see Copier. But I can't use std::unique_ptr and the solution in the code is, not only extremely ugly, but doesn't even work. Plus it calls constructors like crazy.

I tried using std::unique_ptr in a simple function, with std::make_unique (that needs c++14, and I'd rather keep some larger safety margin, but I could also live with it, too). It also doesn't work because it points to Power (if I call z->print() it says 'class Power' has no member 'print'):

std::unique_ptr<Power> call(const Power &p, const int &i)
{
    if (i)
        return std::make_unique<Printer>(p);
    else
        return std::make_unique<Scanner>(p);
}

I don't know how to make this. In short, classes Scanner, Printer, and any other that exist, are dedicated classes that perfom one task, only, unique in their way of computing, and all of them make use of some common variables in Power (besides their own). I don't think it would be very effective to move the common variables to each class because they would only bloat the code, and, as I understand it, "if you can use a storage class instead of repeating the same variable over and over, use it" (not my words, is this true?). Then, I'd like to be able to instantiate those classes, but only have one active at a time, to spare memory.

As an example, suppose one class makes an array of 1mil values, then another makes 1mil different values, and so on. Imagine having that array in memory as many times as there are instantiated classes. I don't want that. The purpose of Copier would have been to call (based on the conditional) only one class at a time. Job done? Call another, but forget anything else that was done before, start anew. And all this to be able to call through only one widget, such as select from list, click&go, that will be added later.


That was a stupid mistake, I forgot to delete public ... after copy-pasting. I also tried the code now (with Copier), it compiles, but still doesn't work, m_x stays empty, even with the very ugly solution of having two Scanner and Printer pointers as member variables inside Copier.


Well, after some tries, I couldn't make what I wanted so I thought to go back to my original idea, even if it meant inheritance. So I came up with this piece of code, where I changed the names to make a bit more sense(?):

#include <iostream>

class Garage
{
protected:
    double m_x; // gas, tires, etc, that all cars use, reside in the Garage
public:
    Garage() {std::cout<<"Garage\n";}
    virtual ~Garage() {std::cout<<"~Garage\n";}
};

class Audi: virtual public Garage
{
public:
    Audi() {std::cout<<"Audi\n";}
    void f(const double &x) { m_x=x; std::cout<<"Audi::f("<<x<<")\n";}
};

class Bmw: virtual public Garage
{
public:
    Bmw() {std::cout<<"Bmw\n";}
    void f(const double &x) { m_x=x; std::cout<<"Bmw::f("<<x<<")\n";}
};

class Driver: public Audi, public Bmw
{
private:
    double m_y; // report of driving, based on m_x
public:
    Driver() {std::cout<<"Driver\n";}
    Driver(const double &x, int i)
    {
        if (i)
            Bmw::f(x);
        else
            Audi::f(x);
        m_y = -m_x;
        std::cout<<"Driver("<<x<<","<<i<<")\n";
    }
    void print() { std::cout << "x=" << m_x << ", y=" << m_y << '\n'; }
};

int main(int argc, char *argv[])
{
    Driver *d {new Driver(1.618, 0)};
    d->print();
    d = new Driver(0.618, 1);
    d->print();
    // even iteration works now
    delete d;
    d = nullptr;    // to be sure it's dead(?)
    for (int i=0; i<2; ++i)
    {
        d = new Driver(3.14, i);
        d->print();
    }
    return 0;
}

Now, this works, but I have a feeling I set a new record on bad code example. Please don't bash me for this, rather point out all the mistakes, or how you would do it to achieve the same result. Still, even if it seems to work as I want, it still calls all the constructors, on all branches, instead of only on the needed ones. I realize (my apologies) I forgot to say that Driver, here, is also responsible for using m_x further, for its m_y (that's why the code is a bit different).


I'd like to point out that I am not fixed in keeping this code, or any other, I am willing to change and adapt, as long as I reach my purpose. But since I am a beginner, I can't make too many combinations, so I am left with presenting whichever result it is that I reached to try and make myself understood. The program above, as it is, when run, gives what I want, even has the possibility of making a loop, which will let me use it much easier in a GUI, later on. The names, as they are, make the most sense in composition, Garage has-a Bmw, and that was what I tried, but I couldn't obtain what I wanted. So, even if this uses inheritance and does not make sense that an Audi is-a Garage, I kept the names to suggest my initial tryout with composition. My main reason for posting this is to show what I would like the program to do. What happens in main() will be used in a GUI, I am thinking of Qt, because I'd like this to run on all 3 major OSes. So having the possibility of calling one car at a time, using it, and also being able to store previous information without having stale objects in memory, only m_x*nr_of_cars, will make it much easier to work with.

a concerned citizen
  • 787
  • 2
  • 9
  • 25
  • Why are you both deriving from Power and have a Power member? – Mat Nov 06 '16 at 09:50
  • 3
    This design is still full of inheritance, even multiple inheritance. And it doesn't make sense, because a scanner and printer are not power, they have power. It would make sense if the base class was something like "machine". You should start to read a book on object-oriented design before getting concerned about things like "saving memory". – Christian Hackl Nov 06 '16 at 09:50
  • @Mat I am using http://www.learncpp.com/cpp-tutorial/102-composition/ as a reference (see lower with Point2D and Creature). #ChristianHackl If I change Power to Garage, Scanner and Printer to Audi and BMW, and Copier to Driver, would those make more sense in terms of composition? – a concerned citizen Nov 06 '16 at 09:55
  • @aconcernedcitizen: that page doesn't have that. – Mat Nov 06 '16 at 09:59
  • @aconcernedcitizen: You are still confusing composition with something else. Composition means e.g. that `Scanner` has a `Power` **member variable**. – Christian Hackl Nov 06 '16 at 10:03
  • @Mat Then I am wrong, but I see `Point2D m_location;` as a private member variable, and a `const Point2D &location` passed as an argument to the constructor, where `m_location {location}`. Is it not the same as `Power m_power;`, passing `const Power &p`, and using `m_power {p}`? – a concerned citizen Nov 06 '16 at 10:03
  • You're using inheritance **and** a private member of the same type. – Mat Nov 06 '16 at 10:04
  • @ChristianHackl But, isn't that what I did with `Power m_power;`? Oh now I see, I left `public` from copy-pasting! I am sorry, I;ll correct it right now. – a concerned citizen Nov 06 '16 at 10:04
  • @aconcernedcitizen: Of course, but you *also* inherit from `Power`. Mind that there are patterns where it makes sense to both contain and inherit from a class (e.g. the classical Decorator GoF pattern), but I find their use cases to be quite rare, and yours here is certainly not a convincing one, either. – Christian Hackl Nov 06 '16 at 10:07

2 Answers2

1

Here is one way to do it.

{ // scope begins
   Printer p; // note, no pointers
   p.print(); 
} // scope ends, p is gone

Here we have an object that appears, does one thing once, and disappears.

Here is another

boost::variant<Printer,Scaner,Copier> psc(Printer());
psc.get<Printer>().print();
psc = Scaner(); // the printer is gone
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Why not `std::variant`? – Christian Hackl Nov 06 '16 at 10:14
  • If I do that, how will I be able to call such a thing later on, through a widget? – a concerned citizen Nov 06 '16 at 10:15
  • 1
    @ChristianHackl c++17 is not here yet. – n. m. could be an AI Nov 06 '16 at 10:18
  • @aconcernedcitizen (var.1) you put it inside a function. (var.2) you make a member variable of the variant type. – n. m. could be an AI Nov 06 '16 at 10:19
  • @n.m.: True, I somehow thought it was part of C++14. – Christian Hackl Nov 06 '16 at 10:23
  • @n.m. That's what I tried with `Copier` and `std::unique_ptr`, but it didn't work. I searched now for "variant type" and, apparently, it's only for Windows? It's true I didn't mention it, but I'd like this to be run on all 3 major OSes, so even `boost` seems like an unneeded external dependency. To exemplify, instead of using MPFR (which I did, until a point), I am trying to make my own small big number header, even if it will be slower, just to avoid being dependent on external libraries. – a concerned citizen Nov 06 '16 at 10:56
  • @n.m. If I were to iterate through objects, that is, call `Scanner`, save `m_x` in a vector, for plotting (or comparisons, for example), then call `Printer`, save `m_x`, etc, how could I achieve this with your methods? To better view it, think of it in the comment here (the 2nd part): https://stackoverflow.com/questions/40447930/instantiate-only-one-class-at-a-time-out-of-a-group-of-classes-to-save-memory#comment68141847_40447930 . How could I iterate through the cars? – a concerned citizen Nov 06 '16 at 11:27
  • It is not at all clear what you mean by iterating, since the objects are of unrelated types and cannot be placed together in the same container. How can you iterate over an int, a double, and a string, and print them all? Same thing here I guess. – n. m. could be an AI Nov 06 '16 at 13:13
0

Use some std::unique_ptr constructor:

std::unique_ptr<Power> 
call(const Power &p, const int &i) {
  if (i)
    return std::unique_ptr<Power>(new Printer(p));
  else
    return std::unique_ptr<Power>(new Scanner(p));
}

Perhaps what you really want is a tagged union. Follow the rule of five. See this for inspiration.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547