user2012732
user2012732

Reputation: 121

returning an array of vectors in C++

I'm having difficulty returning an array of string vectors. I have a function:

std::vector<std::string>* generatecVec(std::vector<std::string> strVec){
  std::vector<std::string> cVec[3];
  cVec[0].push_back("Test11");
  cVec[0].push_back("Test12");
  cVec[0].push_back("Test13");
  cVec[1].push_back("Test21");
  cVec[1].push_back("Test22");
  cVec[1].push_back("Test23");
  cVec[2].push_back("Test31");
  cVec[2].push_back("Test32");
  cVec[2].push_back("Test33");
  return cVec;
}

and later I use the function like

std::vector<std::string> *cVec = generatecVec(strVec);
for(std::vector<string>::iterator it = cVec[0].begin(); it != cVec[0].end(); ++it) {
    std::cout << *it;
}

but I keep getting segmentation fault. I realize I must be using pointers improperly, but how might I fix this? I use the array of vectors because it is very easy to refer to it by index (I only need three, non dynamic). Thanks!

Upvotes: 1

Views: 12327

Answers (4)

Moataz Elmasry
Moataz Elmasry

Reputation: 2519

As the others already explained, you are returning a local reference, that is garbage outside the function scope. As a rule of thumb I try to avoid raw pointers as much as possible, since eventually I forget to delete a pointer after usage, or to initialize a pointer to start with.

When you return a std::array or std::vector you invoke the copy constructor and receive a fresh copy of the vector,array,etc... I personally tend to use boost shared_ptr for these situations, as they overcome most disadvantages that come with the classic C pointers

Upvotes: 1

Slava
Slava

Reputation: 44238

It is strange that you use vector of string when you need collection of strings, but pointer when you need collection of vectors. Using typedef should help with abstraction of details and see possible solution:

typedef std::vector<std::string> strings;
typedef std::vector<strings> strings_seq;

strings_seq generateVec()
{
  strings_seq cVec( 3 );
  cVec[0].push_back("Test11");
  cVec[0].push_back("Test12");
  cVec[0].push_back("Test13");
  cVec[1].push_back("Test21");
  cVec[1].push_back("Test22");
  cVec[1].push_back("Test23");
  cVec[2].push_back("Test31");
  cVec[2].push_back("Test32");
  cVec[2].push_back("Test33");
  return cVec;
}

Upvotes: 0

Seth Carnegie
Seth Carnegie

Reputation: 75130

You're returning a pointer to an automatic array, which is destroyed as it goes out of scope and your pointer points to a destroyed array full of destroyed vectors.

Use std::array and return it by value:

// This means: std::array of 3 std::vector<string>
//   Type--VVVVVVVVVVVVVVVVVVVVVVVV  V-- Array size
std::array<std::vector<std::string>, 3> generatecVec(std::vector<std::string> strVec){
  return { {
     { "Test11", "Test12", "Test13" },
     { "Test21", "Test22", "Test23" },
     { "Test31", "Test32", "Test33" }
  } };
}

auto cVec = generatecVec(strVec);
for(auto it = cVec[0].begin(); it != cVec[0].end(); ++it) {
    std::cout << *it;
}

Upvotes: 0

juanchopanza
juanchopanza

Reputation: 227390

You are returning a pointer to something that only lives in the scope of the function. Once the function is done, cVec vanishes and the caller is left with a dangling pointer. I suggest returning an object that can actually be copied, such as an std::array<std::vector<std::string> 3>.

#include <array> // for std::array

std::array<std::vector<std::string>,3> generatecVec(/*std::vector<std::string> strVec*/){
  std::array<std::vector<std::string>,3> cVec;
  cVec[0].push_back("Test11");
  cVec[0].push_back("Test12");
  cVec[0].push_back("Test13");
  cVec[1].push_back("Test21");
  cVec[1].push_back("Test22");
  cVec[1].push_back("Test23");
  cVec[2].push_back("Test31");
  cVec[2].push_back("Test32");
  cVec[2].push_back("Test33");
  return cVec;
}

I have commented out strvec here because it seems to play no role in the function.

You can then use it like this (C++11 range based for loop syntax):

auto cVec = generatecVec(); // no strVec because it doesn't play any role
for(auto it = cVec[0].cbegin(); it != cVec[0].cend(); ++it) {
    std::cout << *it;
}

Note that the push_backs may not be necessary if your compiler supports C++11 initializer list initialization.

If your compiler doesn't support std::array, try std::tr1::array from , or boost::array.

Upvotes: 5

Related Questions