1

Recently I was working on a project that involved passing arguments by value in C++ and something strange was happening when trying to access the argument fields. The code looked like this:

int main (){
    int sizes[] = {2, 3};
    Topology top;
    top.set_dim(2); // 2D topology
    top.set_sizes(sizes); // 2 rows and 3 columns

    Architecture arch;
    arch.set_topology(top);
}

The Topology class looks like this (it does not have a copy constructor, so I assume it will be generated automatically by the compiler and will be a shallow one, only copying the pointer address, not the data inside):

class Topology {
    public: 
        Topology();
        ~Topology();

        void set_dim(int);
        void set_sizes(int*);

        int get_dim();
        int* get_sizes();

    private:
        int dim;
        int *sizes;
};

Topology::Topology(){

}
Topology::~Topology(){ 
    if (sizes != NULL) 
        delete sizes; 
}
void Topology::set_dim(int dim_){
    dim = dim_;
}
void Topology::set_sizes(int *sizes_){ 
    sizes = new int[dim];
    for (int i = 0; i < dim; i++){
        sizes[i] = sizes_[i];
    }
}
int Topology::get_dim(){
    return dim;
}
int* Topology::get_sizes(){ 
    return sizes;
}

The Architecture class is the following:

class Architecture {
    public: 
        Architecture();
        ~Architecture();

        void set_topology(Topology top);

    private:
        Parallelization p;
};

Architecture::Architecture(){

}
Architecture::~Architecture(){

}
Architecture::set_topology(Topology top){
    p.set_topology(top);
}

Finally, the Parallelization class:

class Parallelization{
    public:
        Parallelization();
        ~Parallelization();

        void set_topology(Topology top);
    private:
};

Parallelization::Parallelization(){

}
Parallelization::~Parallelization(){

}
void Parallelization::set_topology(Topology top){
    int *s = top.get_sizes();
    for (int i = 0; i < top.get_dim(); i++){
        std::cout << s[i] << " "; // here it prints different numbers, like the vector was never initialized [23144, 352452]
    }
}

Shortly, I have a Topology object that gets passed to the Architecture and then to the Parallelization class and there I want to see the internal values from the topology sizes vector. Every time the object is passed by value the implicit copy constructor is called and copies only the dim field and the address of the sizes field, not the whole vector. I wonder why I receive different values, because the vector is still in memory, so it should print the same values.

I mention that if I implement a deep copy-constructor inside the Topology class it works just fine, or if I send the top object by reference.

Am I missing something? What could be the cause of this behavior?

Roxana Istrate
  • 632
  • 1
  • 6
  • 18
  • Fyi, `delete sizes` should be `delete [] sizes;`, and of course read this: ["What is the Rule of Three?"](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). Regarding the "numbers", ask yourself what happens without the deep copy after the *first* `Topology` object is destroyed? What happens to the other `Topology` objects that are toting around the same pointer due to shallow copies? And what happens when *they* `delete sizes` on a now-dangling pointer? Finally, ask yourself why you're not using `std::vector` in the first place. – WhozCraig Jun 29 '15 at 09:26
  • Because yor destructor deletes it. Then it deletes it again. You can't outsmart the rule of three/five. – n. m. could be an AI Jun 29 '15 at 09:31
  • your example [does not show the problem you mention](http://coliru.stacked-crooked.com/a/558b2edb49cf223e), I just commented out `delete sizes;` (see comment by @WhozCraig) – m.s. Jun 29 '15 at 09:32
  • 1
    And your code is ultimately unsafe regardless. you never provide initial values to `dim`, nor `sizes`, in the constructors for `Topology`. Their values are *indeterminate* after successful default construction. Unless `set_sizes` is invoked (itself a recipe for a memory leak if invoked multiple times), the `delete sizes` in the destructor invokes *undefined behavior*. My prior comment closing statement stands. ultimately given what is show here would you be better off scrapping `Topology` entirely and using `typedef std::vector Topology;`. There is little reason to reinvent the wheel. – WhozCraig Jun 29 '15 at 09:32
  • Ok, about the memory leaks, I did not write the entire code, just some portions enough to let you understand the situation, thank you anyway. The Topology has other fields, i cannot just typedef a vector. :) but the previous comments made me understand he problem. Thank you – Roxana Istrate Jun 29 '15 at 09:35
  • Still, I have another question. The first Topology object that goes out of scope is the one in this function: Parallelization::set_topology(Topology top), so when the vector elements are printed the vector is not deleted anyway.. And to check double deletion I have a a if-condition on a NULL vector in the destructor, so I suppose it should be fine – Roxana Istrate Jun 29 '15 at 09:45
  • "And to check double deletion I have a a if-condition on a NULL vector in the destructor, so I suppose it should be fine" . You are very much mistaken. Again, can't beat the rule of three. – n. m. could be an AI Jun 29 '15 at 09:47
  • *And to check double deletion I have a a if-condition on a NULL vector in the destructor, so I suppose it should be fine* OH GOSH THE HORROR. – UmNyobe Jun 29 '15 at 10:01
  • when you don't have anything to explain to help someone understand maybe you should keep it quiet :) – Roxana Istrate Jun 29 '15 at 10:03

0 Answers0