How I can safely reference to an std::vector element? - c++

I will like to create a struct that holds a higher state for other structs.
I tried to create it like this, but I recently found that returning a pointer to an element of an std::vector is not safe, since that pointer can change.
struct Foo {
std::string &context;
std::string content;
Foo(std::string &context, std::string content) : context(context), content(content) {}
}
struct Bar {
std::string context;
std::vector<std::string> manyFoos;
Foo* addFoo(std::string content) {
manyFoos.emplace_back(context, content);
return &manyFoos[manyFoos.size() - 1];
}
}
struct Example {
Bar bar;
Foo* fooA;
Foo* fooB;
Example() {
fooA = bar.addFoo("Hello");
fooB = bar.addFoo("World");
}
}
What could be a good and safe way of doing this?

What could be a good and safe way of doing this?
Saving the couple (vector reference, Foo's index), assuming Foos are only added at the back.
With a bit of syntaxic sugar:
struct Bar {
std::string context;
std::vector<Foo> manyFoos;
struct FooProxy
{
std::vector<Foo>& foos;
std::vector<Foo>::size_type index;
operator Foo() { return foos[index]; }
};
auto addFoo(std::string content) {
manyFoos.emplace_back(context, content);
return FooProxy{manyFoos, manyFoos.size()-1};
}
};
Live example

You can have vector of shared_ptr & return weak_ptr, this way you can make sure correct referencing
#include <iostream>
#include <vector>
#include <memory>
struct Foo {
std::string &context;
std::string content;
Foo(std::string &context, std::string content) : context(context), content(content) {}
};
struct Bar {
std::string context;
std::vector<std::shared_ptr<Foo>> manyFoos;
std::weak_ptr<Foo> addFoo(std::string content) {
auto foo = std::make_shared<Foo>(context, content);
manyFoos.emplace_back(foo);
return foo;
}
};
void printFoo(std::weak_ptr<Foo> foo)
{
// Here we are checking weak_ptr expiry
std::cout << (foo.expired() ? "Foo Expired" : foo.lock()->content) << std::endl;
}
int main() {
Bar bar;
std::weak_ptr<Foo> fooA;
std::weak_ptr<Foo> fooB;
fooA = bar.addFoo("Hello");
fooB = bar.addFoo("World");
printFoo(fooA);
printFoo(fooB);
// erase last element
bar.manyFoos.pop_back();
printFoo(fooB);
return 0;
}
Output:
Hello
World
Foo Expired

Related

Member variable seemingly gets initialized twice

I have the following classes:
Bar.hpp
#include <string>
class Bar
{
public:
Bar(const std::string& info);
std::string getInfo() { return info; }
protected:
private:
std::string info;
};
Bar.cpp:
#include <Bar.hpp>
Bar::Bar(const std::string& info)
: info(info) { }
Foo.hpp:
#include <string>
#include <vector>
#include <Bar.hpp>
class Foo
{
public:
static void printInfoStore();
static Foo* getFoo(const std::string& name);
Foo() {};
std::string getName() { return name; }
std::vector<Bar> getBars();
void addBar(const std::string& info);
protected:
private:
static std::vector<Foo> infoStore;
Foo(const std::string& name);
std::vector<Bar> bars{};
std::string name;
};
Foo.cpp
#include <iostream>
#include <Foo.hpp>
Foo::Foo(const std::string& name)
: name(name) { }
//static
std::vector<Foo> Foo::infoStore(0);
//static
void Foo::printInfoStore() {
std::cout << "InfoStore is { ";
for (Foo& foo : infoStore) {
std::cout << foo.getName() << "=[ ";
for (Bar& bar : foo.getBars()) {
std::cout << bar.getInfo() << " ";
}
std::cout << "] ";
}
std::cout << " }" << std::endl;
}
//static
Foo* Foo::getFoo(const std::string& name) {
for (Foo& foo : infoStore) {
if (foo.getName() == name) {
return &foo;
}
}
Foo* foo = new Foo(name);
infoStore.push_back(*foo);
return foo;
}
std::vector<Bar> Foo::getBars() {
return bars;
}
void Foo::addBar(const std::string& info) {
Bar* bar = new Bar(info);
bars.push_back(*bar);
}
Basically there is a static vector that holds multiple Foo objects each of which has a vector of Bar objects.
Then I have the following main.cpp:
#include <Foo.hpp>
#include <Bar.hpp>
int main(int argc, char *argv[])
{
Foo::printInfoStore();
Foo::getFoo("Foo1")->addBar("info11"); // info11 is not added to Foo1
Foo::printInfoStore();
Foo::getFoo("Foo1")->addBar("info12");
Foo::printInfoStore();
Foo::getFoo("Foo1")->addBar("info13");
Foo::printInfoStore();
Foo::getFoo("Foo2")->addBar("info21"); // info21 is not added to Foo2
Foo::printInfoStore();
Foo::getFoo("Foo2")->addBar("info22");
Foo::printInfoStore();
return 0;
}
The output is the following:
InfoStore is { }
InfoStore is { Foo1=[ ] }
InfoStore is { Foo1=[ info12 ] }
InfoStore is { Foo1=[ info12 info13 ] }
InfoStore is { Foo1=[ info12 info13 ] Foo2=[ ] }
InfoStore is { Foo1=[ info12 info13 ] Foo2=[ info22 ] }
The strange thing is that the first Bars added for each Foo object (info11 and info21) do not get added. My guess is that there might be a second initialization of the bars vector that happens after the parent Foo object but I don't know if this is the case, nor I can find a rationale behind it.
I tried to initialize the bars vector explicitly within the foo constructor but to no avail: the first Bar added is always discarded.
So why does it happen? What's wrong with my code? What can be done to avoid that behavior?
All of your news are causing memory leaks. None of this code should be using new at all, since none of the vectors are holding pointers.
For that matter, since getFoo() always returns a valid object, whether an existing object or newly-pushed object, it should return a Foo& reference to that object rather than a Foo* pointer (you can return a pointer if you really want to, but I would not advise it).
Either way, you would have to make sure that whatever you do return actually refers to an object that is inside of your vector. Your output is not what you expect because the code is NOT doing this correctly, which is the root of your problem.
When you call getFoo() for a non-existent object, you create a new object, then push a copy of that object into your vector, and then return a pointer to the new'ed object, not a pointer to the copied object. So, any values you subsequently store in the new'ed object appear to be discarded when you print the vector later, since the values don't exist in the copied object that is actually inside of the vector. When you call getFoo() again for an existing object, you return a pointer to the copied object that is inside the vector, and you do not create a new object.
On a similar note, getBars() should also return a reference to its vector, not return a copy of its vector by value.
With all of that said, try something more like this instead:
Bar.hpp
#include <string>
class Bar
{
public:
Bar(const std::string& info);
std::string getInfo() const;
private:
std::string info;
};
Bar.cpp:
#include <Bar.hpp>
Bar::Bar(const std::string& info)
: info(info) { }
std::string Bar::getInfo() const
{ return info; }
Foo.hpp:
#include <string>
#include <vector>
#include <Bar.hpp>
class Foo
{
public:
static void printInfoStore();
static Foo& getFoo(const std::string& name);
std::string getName() const;
std::vector<Bar>& getBars();
const std::vector<Bar>& getBars() const;
void addBar(const std::string& info);
private:
static std::vector<Foo> infoStore;
std::vector<Bar> bars;
std::string name;
Foo(const std::string& name);
};
Foo.cpp
#include <iostream>
#include <Foo.hpp>
Foo::Foo(const std::string& name)
: name(name) { }
//static
std::vector<Foo> Foo::infoStore;
//static
void Foo::printInfoStore() {
std::cout << "InfoStore is { ";
for (const Foo& foo : infoStore) {
std::cout << foo.getName() << "=[ ";
for (const Bar& bar : foo.getBars()) {
std::cout << bar.getInfo() << " ";
}
std::cout << "] ";
}
std::cout << " }" << std::endl;
}
//static
Foo& Foo::getFoo(const std::string& name) {
for (Foo& foo : infoStore) {
if (foo.getName() == name) {
return foo;
}
}
infoStore.push_back(Foo(name));
return infoStore.back();
// or, in C++11..14:
// infoStore.emplace_back(name);
// return infoStore.back();
// or, in C++17 onward:
// return infoStore.emplace_back(name);
}
std::string Foo::getName() const {
return name;
}
std::vector<Bar>& Foo::getBars() {
return bars;
}
const std::vector<Bar>& Foo::getBars() const {
return bars;
}
void Foo::addBar(const std::string& info) {
bars.push_back(Bar(info));
// or, in C++11 onward:
// bars.emplace_back(info);
}
main.cpp
#include <Foo.hpp>
#include <Bar.hpp>
int main(int argc, char *argv[])
{
Foo::printInfoStore();
Foo::getFoo("Foo1").addBar("info11");
Foo::printInfoStore();
Foo::getFoo("Foo1").addBar("info12");
Foo::printInfoStore();
Foo::getFoo("Foo1").addBar("info13");
Foo::printInfoStore();
Foo::getFoo("Foo2").addBar("info21");
Foo::printInfoStore();
Foo::getFoo("Foo2").addBar("info22");
Foo::printInfoStore();
return 0;
}
You store copy in your vector (with memory leaks).
Fixed version:
Foo* Foo::getFoo(const std::string& name) {
for (Foo& foo : infoStore) {
if (foo.getName() == name) {
return &foo;
}
}
return &infoStore.emplace_back(Foo{name});
}
void Foo::addBar(const std::string& info) {
bars.emplace_back(info);
}
Demo

C++ range based for loop over vector of pointers. How to capture elements by const reference?

How can I capture the elements by const reference in a range-based for loop over a vector containing pointers?
#include <iostream>
#include <memory>
#include <vector>
class Foo {
public:
Foo(int a) : a{a} {}
int value() const { return a; }
private:
int a;
};
int main() {
std::unique_ptr<Foo> f1 = std::make_unique<Foo>(1);
std::unique_ptr<Foo> f2 = std::make_unique<Foo>(2);
std::vector<Foo *> v;
v.push_back(f1.get());
v.push_back(f2.get());
//for (const Foo &f : v) { // <-- This does not work..
// std::cout << f.value() << '\n';
//}
for (Foo *f_ptr : v) {
const Foo &f = *f_ptr; // <-- I would like to avoid this
std::cout << f.value() << '\n';
}
return 0;
}
I would like to avoid creating a separate variable just to construct the reference:
for (Foo *f_ptr : v) {
const Foo &f = *f_ptr; // <-- I would like to avoid this
// ...
}
Is it possible to have f assigned directly in for loop condition? E.g. something like:
for (const Foo &f ...) { }
The vector contains pointers, so that's what you get. If you want something else you need to loop over a different object.
C++20 ranges offers a way to do that. It wouldn't be very hard to roll your own variant.
#include <iostream>
#include <memory>
#include <vector>
#include <ranges>
class Foo {
public:
Foo(int a) : a{a} {}
int value() const { return a; }
private:
int a;
};
int main() {
std::unique_ptr<Foo> f1 = std::make_unique<Foo>(1);
std::unique_ptr<Foo> f2 = std::make_unique<Foo>(2);
std::vector<Foo *> v;
v.push_back(f1.get());
v.push_back(f2.get());
for (const auto& f : std::ranges::views::transform(v, [](auto ptr) -> const Foo& { return *ptr; })) {
std::cout << f.value() << '\n';
}
return 0;
}

Wrapping array of C objects into C++ class

I have C library with such API:
extern "C" {
typedef struct Opaque Opaque;
Opaque *foo_new();
void foo_delete(Opaque *);
int foo_f(Opaque *, int);
}
To simplify it's usage I wrap it in such way:
class Foo final {
public:
Foo() { self_ = foo_new(); }
~Foo() { foo_delete(self_); }
//code for copy/move constructor and operator=
int f(int a) { return foo_f(self_, a); }
private:
Opaque *self_;
};
All great, but then I have to wrap array of this opaque objects:
extern "C" {
typedef struct OpaqueArray OpaqueArray;
OpaqueArray *calc_foo_array();
void foo_array_delete(OpaqueArray *);
Opaque *foo_array_elem(OpaqueArray *, size_t i);
}
So I need implement class FooArray:
class FooArray final {
public:
??? operator[](const size_t i) {
auto obj = foo_array_elem(self_, i);
???
}
private:
OpaqueArray *self_;
};
But what should I return as result of operator[]?
I can create Foo from Opaque *, but then Foo::~Foo() is free part of array,
what is wrong. I can create FooRef that would be exactly the same as Foo,
but do not call foo_delete, but actually I have several such C classes,
and I prefer do not create so many code duplicates.
May be I can somehow use reinterpret_cast, because of sizeof(Foo) = sizeof(Opaque *) and return Foo & from operator[], but Foo & actually is Opaque **,
so I need somewhere hold Opaque to make it address stable.
May be there is some standard solution for such kind of problem?
You could modify your Foo class so that it can hold a pointer that it doesn't own.
class Foo
{
public:
Foo()
{
self_ = foo_new();
m_owned = true;
}
Foo(Opaque *pOpaque)
{
self_ = foo_new();
m_owned = false;
}
~Foo()
{
if (m_owned) foo_delete(self_);
}
//code for copy/move constructor and operator=
int f(int a) { return foo_f(self_, a); }
private:
bool m_owned;
Opaque *self_;
};
class FooArray
{
public:
Foo operator[](const size_t i)
{
return Foo(foo_array_elem(self_, i));
}
private:
OpaqueArray *self_;
};
I'd do it using proposed by You FooRef but a bit differently:
class FooRef {
public:
FooRef (Opaque *o) { self_ = o; }
int f(int a) { return foo_f(self_, a); }
protected:
Opaque *self_;
};
class Foo : public FooRef {
public:
Foo() { self_ = foo_new(); }
//code for copy/move constructor and operator=
~Foo () { foo_delete(self_); }
};
This solution avoids code duplication and allows you to safely return Foo from array. And by the way you got mechanism to simply create FooRef from Foo. Now you can do just:
class FooArray final {
public:
FooRef operator[](const size_t i) {
return FooRef(foo_array_elem(self_, i));
}
private:
OpaqueArray *self_;
};
I think that this should do the trick in elegant way.

return vector of objects to a function

What is the correct syntax to do this? Surely I made some stupid mistake ... unfortunately I'm trying to better understand the vectors. I know that I created an unnecessary pointer, but I need to understand the syntax.
#include <iostream>
#include <vector>
class otherClass
{
public:
otherClass(int x):value(x)
{
//ctor
}
int getValue()
{
return value;
}
private:
int value;
};
class MyClass
{
public:
MyClass(int x)
{
obj = new std::vector<otherClass>(x,otherClass{5});
}
otherClass getVector()
{
return obj; //HERE FIRST ERROR <---------------
}
private:
std::vector<otherClass>*obj;
};
void doSomething(otherClass*obj)
{
std::cout << obj->getValue() << std::endl;
}
int main()
{
MyClass*aClass = new MyClass(10);
doSomething(aClass->getVector()); //HERE SECOND ERROR <---------------
return 0;
}
Errors that I get when compiling:
First:
error: invalid conversion from 'std::vector<otherClass>*' to 'int' [-fpermissive]
Second:
error: cannot convert 'otherClass' to 'otherClass*' for argument '1' to 'void doSomething(otherClass*)'
First of all, there is no point in using any pointer here. None!
Second, your getters should be qualified const, and return const references for heavy objects like your vector. It prevents an useless copy.
int getValue() const
// ^^^^^
{
return value;
}
within otherClass, and
class MyClass
{
public:
MyClass(int x) : obj(x, otherClass{5}) // construction here
{ }
std::vector<otherClass> const & getVector() const
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^
{
return obj;
}
private:
std::vector<otherClass> obj; // no pointer, just a vector
};
Then in the main:
MyClass aClass(10);
What you want to do with doSomething() is unclear. With your code doSomething(aClass->getVector()) you're supposed to handle the returned vector of otherClasses. So it should be:
void doSomething(std::vector<otherClass> const & obj)
I let you write its code.
just say what you want to return
std::vector<otherClass> *getVector()
{
return obj;
}
or
std::vector<otherClass> getVector()
{
return *obj;
}

Is const_cast on pointer to member safe?

In the following code, a non-const method of an object calls a const-method of the same object that returns a const-pointer to the object's field, and then this returned pointer is casted to a non-const version — it's a technique similar to one in this answer: Elegant solution to duplicate, const and non-const, getters? [duplicate]. However, since I put pointers into a complex data structure, I am not sure it will actually work. Will it?
class Struct {
private:
Field f, g;
std::map<std::string, const FieldBase*> const_fields_t;
std::map<std::string, FieldBase*> fields_t;
public:
const_fields_t get_fields() const {
return const_fields_t { { "f", &f }, { "g", &g } };
}
fields_t get_fields() {
const Foo* = this;
fields_t result;
foreach(auto& v : const_this->get_fields()) {
result[v->first] = const_cast<FieldBase*>(v->second);
}
return result;
}
};
Yes, (I cleaned up your code a bit):
#include <string>
#include <functional>
#include <iostream>
#include <map>
using namespace std;
class FieldBase {public: virtual string get_data() = 0; };
class Field : public FieldBase { public: string data; virtual string get_data() { return data; } };
class Struct {
public:
Field f, g;
typedef std::map<std::string, const FieldBase*> const_fields_t;
typedef std::map<std::string, FieldBase*> fields_t;
public:
const_fields_t get_fields() const {
cout << "const get_fields() called" << endl;
return const_fields_t { { "f", &f }, { "g", &g } };
}
fields_t get_fields() {
cout << "non-const get_fields() called" << endl;
auto const_this = static_cast<const Struct*>(this);
fields_t result;
for(auto& v : const_this->get_fields()) {
result[v.first] = const_cast<FieldBase*>(v.second);
}
return result;
}
};
int main ()
{
Struct a;
a.f.data = "Hello";
a.g.data = "World";
Struct::fields_t obj = a.get_fields();
cout << obj["f"]->get_data() << endl;
cout << obj["g"]->get_data() << endl;
}
Live example
You basically have the const function act like a gateway to get the values you need and then cast away the constness. That will also work for your purpose since the pointers are going to be de-consted and stored in the new map.
Keep in mind that there might probably be a better solution not involving all the above copying around.