Search code examples
c++classstructure

Class methods self call best practice


Consider these following two C++ examples (even if language does not matter).


First, the Foo class, which call its own methods like getSize(), isPosValid(), getElement().

class Foo {
private:
    std::vector<int> elements_ { };

public:
    size_t getSize() const {
        return elements_.size();
    }

    bool isPosValid(const size_t pos) const {
        return pos < getSize();
    }

    int getElement(const size_t pos) const {
        if (!isPosValid(pos)) {
            return 0;
        }

        return elements_[pos];
    }

    // Here we could use elements_.back(), but this is just for the example.
    int getLastElement() const {
        if (getSize() == 0u) {
            return 0;
        }

        return getElement(getSize() - 1u);
    }

    bool operator==(const Foo& rhs) const {
        return getSize() == rhs.getSize();
    }

    bool operator!=(const Foo& rhs) const {
        return !(*this == rhs);
        // OR maybe : return !operator==(rhs);
    }
}

Then, the Bar class, which don't call his own methods, but has kind of code duplicate.

class Bar {
private:
    std::vector<int> elements_ { };

public:
    size_t getSize() const {
        return elements_.size();
    }

    bool isPosValid(const size_t pos) const {
        return pos < elements_.size();
    }

    int getElement(const size_t pos) const {
        if (pos >= elements_.size()) {
            return 0;
        }

        return elements_[pos];
    }

    // Here we could use this->elements_.back(), but this is just for the example.
    int getLastElement() const {
        if (elements_.size() == 0u) {
            return 0;
        }

        return elements_[elements_.size() - 1u];
    }

    bool operator==(const Bar& rhs) const {
        return elements_.size() == rhs.elements_.size();
    }

    bool operator!=(const Bar& rhs) const {
        return elements_.size() != rhs.elements_.size();
    }
}

As you can see, Foo and Bar have different architectures.

Theses examples are very basic, but we can start to see some issues with both of them.

For Foo, getElement() verify the position, so if we call it when we already made the verification (like in getLastElement()), then it is done twice.

And for Bar, there is a lot of "code duplication", and if we want to modify how an element is accessed we have to do it in all methods that does the access. (Instead of just modifying the getElement() in the Foo example).


Which design is best maintainable and readable. Is there a way to fix this design issue, without mixing both styles and keeping consistent?

And kind of off topic question, is it better in C++ call operators inside a class like this operator=(XXX) or like this *this = XXX; (like in the Foo::operator!= example) ?


EDIT: Removed this-> qualifiers as suggested in comment.


Solution

  • In general, the first one is a better design. You always want to factor out common code (like the size checking) and reuse it in your class. Your example is trivial but in the real world the check may be more than just looking at the size and you would want to put the common logic in one place. That's just good software design.

    For the logical operators, you always want to define != in terms of == for the same reason. Similarly for other logical operators.

    Also, use the more natural equality expression. Using the operator=() function is much less readable. Even for an experienced C++ programmer in my opinion.