-2

If we have a vector of struct pointer MyInfo* (allocated on heap). Then we can check vec[i] == NULL to know whether there is a struct in the vec[i], like this, if (vec[i] != NULL) //then do some processing

However, if we allocate MyInfo on stack instead of on heap, then we have vector<MyInfo> as shown below. I guess each vec[i] is initialized by the struct default constructor. How do you check whether vec[i] contains a non-empty struct similar to above NULL pointer case, like if (vec[i] contains valid struct) //then do some processing

My code is below

#include <iostream>     // std::cout
#include <string>
#include <vector>

using namespace std;

struct MyInfo {
    string name;
    int age;
};

int main () {
    vector<MyInfo> vec(5);
    cout << "vec.size(): " << vec.size() << endl;
    auto x = vec[0];
    cout << x.name << endl; //this print "" empty string
    cout << x.age << endl; //this print 0

    return 0;
}
thinkdeep
  • 945
  • 1
  • 14
  • 32
  • @Scheff It's guaranteed to be `0` because the elements of `std::vector` are value-initialized here. – songyuanyao Jul 06 '19 at 09:50
  • @Scheff `MyInfo`'s constructor doesn't do that, it's [value-initialization](https://en.cppreference.com/w/cpp/language/value_initialization): *if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;* Note the part about zero-initialization. – songyuanyao Jul 06 '19 at 09:57
  • @songyuanyao Aah. The part of value initialization was missing. :-) (do need this rarely in daily business, however) Thanks, for the enlightening. – Scheff's Cat Jul 06 '19 at 09:58
  • Thanks, then for the vector of struct case, how to check the vec[i] indeed contain non default constructed struct? Or we can only resort to use vector of struct pointers? – thinkdeep Jul 06 '19 at 09:59
  • That's difficult IMHO. Nothing hinders you to store an empty string into `name` and 0 into `age`. How to distinguish this from default _value initialization_? For this, you would need a third flag which is set in an overloaded assignment operator (to mark the instance as "touched"). And, of course, this wouldn't make sense without making member variables `private`. – Scheff's Cat Jul 06 '19 at 10:01
  • [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional) could be a solution. – Scheff's Cat Jul 06 '19 at 10:05
  • 4
    `std::vector` does not allocate the elements on stack either. The data of an `std::vector` will (under the condition the system uses the concept of stack and heap) always be on the heap. – t.niese Jul 06 '19 at 10:09
  • If you had `MyInfo *vec[5]` it translates kind-of to `std::vector vec(5)` – KamilCuk Jul 06 '19 at 10:11
  • @KamilCuk This in mind - I would prefer `std::vector` for two reasons: 1.) All the trouble with pointers is away. 2.) All instances are stored contiguous which might have positive effects on performance (though it might make re-allocations more expensive). – Scheff's Cat Jul 06 '19 at 10:41
  • @t.niese Do you mean vector of struct won't allocate the struct on heap? Then, we need to delete the struct for freeing memory in heap, then? – thinkdeep Jul 06 '19 at 13:22
  • @coder The number of elements in an `std::vector` is not fixed but dynamic, and it is required that its elements are stored contiguously. So there is no way that its elements are stored on the stack. The vector is responsible for freeing up the allocated memory and calling the destructor for the stored type. Whether you need to delete an element that exists in the heap depends on how it was created on the heap and who currently holds the ownership and responsibility for it. – t.niese Jul 06 '19 at 14:12

1 Answers1

2

There are some options you can use. The first and easiest one, is to define a value to each (or for one) of your struct's variables, that will point that the struct is not initialized yet. In this case, age should be large or equal to 0, to be logicly straight. So, you can initialize it to -1, like this:

struct MyInfo {
    string name;
    int age = -1;
};
// Or
struct MyInfo {
    string name;
    int age;
    MyInfo() : name(""), age(-1) {} // Use constructor
};

Now, in your main function, it will print in the age the value -1. Also, you can see the empty of the name variable as a sign for it too.

Another way might be using flag and get/set operations to indicate when the variables are initialize:

struct MyInfo {
private:
    std::string _name;
    int _age;
    bool age_initialize = false;
    bool name_initialize = false;

public:
    void name(const std::string &name_p) { _name = name_p; name_initialize = true; }
    void age(int age_p) { _age = age_p; age_initialize = true; }
    void init(int age_p, const std::string &name_p) { age(age_p); name(name_p); }
    bool is_initialize() { return name_initialize && age_initialize; }
    int age() { return _age; }
    std::string name() { return _name; }
};

int main() {
    std::vector<MyInfo> vec(5);
    std::cout << "vec.size(): " << vec.size() << std::endl;

    auto x = vec[0];
    std::cout << x.is_initialize() << std::endl; //this print 0
    std::cout << x.name() << std::endl; //this print "" empty string
    std::cout << x.age() << std::endl; //this print 0

    return 0;
}

You can also throw an exception when calling int age() of std::string name() function, if those values are not initialize yet:

struct MyInfo {
private:
    /* ... */

public:
    /* ... */
    int age() {
        if (!age_initialize) throw std::runtime_error("Please initialize age first.");
        return _age;
    }
    std::string name() {
        if (!name_initialize) throw std::runtime_error("Please initialize name first.");
        return _name;
    }
};
Coral Kashri
  • 3,436
  • 2
  • 10
  • 22