1

I am trying to design a group of related classes. For example:

  • one table class holds a vector of integer, and has a function returning integers from the vector
  • another table class holds a vector of double, and has a function returning doubles from the vector

Ideally I'd like to have the two classes share the same pointer type, and have the same interface so that they can constructed, and used in a similar way by client. The only difference is that one class is expected to return integer, and the other double.

I have read a few books on design patterns, and I understand the difference between inheritance vs composition. Though I am still a beginner on class design and I suspect I missed something obvious. Below are my questions:

  1. Is it a good practice to have undefined data member or function members? In the sample coding below. The child class will define only part of the data and function member from the base class.
  2. Ideally I would like to have a common interface between the two child classes. however, since function get_value1 from one class returns integer, the other get_value2 from another class returns double, I have to give them a different function name. This is not critical, but can it be avoided?
  3. Should I avoid inheritance and simply create two different classes? especially when the two classes do not share the same user interface?

 

class Base_Table
{
    std::vector<int> vec_int_;
    std::vector<double> vec_dbl_;
    virtual int get_value1(int);
    virtual double get_value2(int i);
};

class Int_Table : public Base_Table
{
    std::vector<int> vec_int_;
    virtual int get_value1(int i) override
    {
        return vect_int_[i];
    }
 };

class Frac_Table : public Base_Table
{
    std::vector<double> vec_dbl_;
    virtual double get_value2(int i) override
    {
        return vect_int_[i];
    }
};
6
  • 1
    What should happen if you call get_value2 on an Int_Table object? How are these classes going to be used? Do they need to have a common base class? Why do you have vectors in both the base and derived classes? Apr 5, 2018 at 4:06
  • 2
    Your title makes no sense. Please fix. Apr 5, 2018 at 5:08
  • 3
    Well, if one class returns integers and the other returns doubles they do not have the same interface.
    – Mael
    Apr 5, 2018 at 5:49
  • @Martin Maar yes, I need to fix the title. But I can't find the link for edit. I only saw share, delete, and flag. Can you help?
    – DavidY
    Apr 5, 2018 at 10:12
  • @DavidY I see. I just fixed it. Apr 5, 2018 at 11:03

3 Answers 3

4

Create a template class. Your use-case is why templates were created. For your example this is quite simple:

template <class T> 
class Table
{
    std::vector<T> vec;
    T get_value(int i) const { 
        return vec[i];
    }
}

But why are you using "get_value" instead of defining operator[] like this:

    T operator[](int i) const {
         return vec[i];
    }
6
  • 2
    or just use std::vector<T>
    – Caleth
    Apr 5, 2018 at 16:01
  • 1
    The only drawback is that different template instantiations are different types, so they wouldn't be able to, for example, store instances of both types inside the same container, though it's not clear from the original post whether this is something they would really need to do. Apr 5, 2018 at 16:06
  • @SeanBurton: it appeared OP was using inheritance to factor out common implementation. Nothing in the question indicated that the variants had to inherit some common base type. Apr 7, 2018 at 7:50
  • @kevin Thanks for the advice. This code is so clean! But if I have to write specialization for integer and double separately, would that be similar to writing two separate classes without template? would template still have advantage in that case? Is it correct that I should use template when I have different Ts that can share same class?
    – DavidY
    Apr 7, 2018 at 19:20
  • @SeanBurton and Caleth, I may have oversimplified my case, and it may not be clear. Thanks for reminding me that different instantiations are different types. It would be great in my situation to put them in the same container, but I guess I can' do that.
    – DavidY
    Apr 7, 2018 at 19:26
1

C++ design - vector classes with different element types

It looks like you want to have some sum type or tagged union. Perhaps you want to use std::variant.

Notice that most containers are homogeneous. In particular, all components of a std::vector have the same type (so stricto sensu your question does not make any sense). But that type could of course be a tagged union.

It is unclear if you want a vector of tagged unions (e.g. std::vector<std::variant<int,float>> ...), or a tagged union of vectors (like std::variant<std::vector<int>,std::vector<float>>). These are different!

You could also have your own class containing some union (and implement your tagged union above that). Then you need to follow the rule of five.

You might also use placement new and manage some memory yourself.

(what you really want to do is actually unclear; please reason by specifying precisely some abstract data type, e.g. the signature of the desired class - its public member functions - and its behavior)

Notice that int and double are different and somehow incompatible POD types. They generally have different sizes, different alignments, and are handled differently (e.g. sit in different processor registers; so the ABI needs to know about them). So casting between int and double usually requires some conversion (at the machine level) and is not "free" (takes at least one machine instruction, and could lose information). and an aligned sequence of bytes has different meaning when considered as an array of int and an array of double...

1
  • You read my mind...I actually would hope I could put different classes defined by myself in the same container, something like: std::vector<std::variant<Table<int>, Table<float>>>). Do you think this would work. Are there any rules I have to following when implementing the Table<T>?
    – DavidY
    Apr 7, 2018 at 19:33
-1

Inheritance does not apply. You could do it with a so called template class but I would not recommend it. If T is a value type like in your case you typically want to perform arithmetic operations on it and these do not work well with T. So you would be better of treating integer tables as different from float tables and have different, dedicated and independent classes for them. That only share methods names for readability/consistency.

Edit: Apparently, as pointed out by Kevin, the particular limitation described above does not apply to C++ (it is valid for C#). You would still want to consider whether all the operations you are going to implement will be meaningful for integers and floating type numbers alike though. This may be hard to foresee. I mean, the "smart" solution may bite you in the arse later.

If for instance, if you needed to create an operation that will give you the average value in a column, that would require you to apply some rounding logic if you were dealing with integers. But not if you were dealing with floats. You would not be so happy with your fancy template solution at that point.

9
  • A template class would be suited for OP's particular example, but I agree, it seems like the eventual road leads to calculation of upper and lower values which means having to know their types.
    – Neil
    Apr 5, 2018 at 6:24
  • @Martin Maat, Neil Mael, and 1201ProgramAlarm -- I feel the answers are particularly helpful to me as I am trying to apply the theories in practice for the first time. Thanks!
    – DavidY
    Apr 5, 2018 at 14:56
  • 1
    A template should work just fine. Why do you say that "arithmetic operations... do not work well" ? Apr 5, 2018 at 15:52
  • @kevin If you have a class Vector<T> that declares a couple of members of type T (say a and b), I don't think you can do a + b. You cannot in C# and I would be surprised if you could in C++. Apr 5, 2018 at 16:04
  • 1
    @Martin Maat: be surprised. C++ templates are a Turing-complete meta-programming language, much more powerful than C# or Java generics. Apr 6, 2018 at 20:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.