Accessing vector in class - c++

If I have a vector as a private member in my class, what's the best way to access it? For example, take the following simple class
class MCL{
private:
std::vector my_vec;
public:
// Include constructor here and other member functions
}
What's the best way to access my_vec? Specifically, I would like to use a getter function to access it.

return it by const reference, or just by reference if you want to allow changing.
const std::vector<T> & getVector() const
{
return vector;
}
usage:
const std::vector<T> &v = myClass.getVector();

Create a public function called
std:vector getMyVec() {return my_vec;}

Depending on the semantics of your class, you may want to implement operator[]:
T& operator[](int i) {
return my_vec[i];
}
This way you can user [] to access the contents of your vector:
MCL a;
a[0] = 3;
std::cout << a[0] << std::endl;
Note that this may be considered abuse of operator[] or bad practice, but it is up to the developer to judge if this construct fits in the class, depending on its semantics.
Also note that this solution does not provides a way to insert or delete elements from the vector, just access to the elements already there. You may want to add other methods to do these or to implement something like:
T& operator[](int i) {
if(my_vec.size() < i)
my_vec.resize(i+1);
return my_vec[i];
}
Again, it is up to the semantics of your class and your usage pattern of it. This may or may not be a good idea.

Related

How to return a private pointer to a list of pointers as const?

I have a pointer to a list of pointers, as a private variable. I also have a getter that returns the pointer to the list. I need to protect it from changes.
I couldn't find how to use reinterpret_cast or const_cast on this.
class typeA{
shared_ptr<list<shared_ptr<typeB>>> l;
public:
shared_ptr<list<shared_ptr<const typeB>>> getList(){return (l);};
};
The compiler returns:
error: could not convert ‘((typeA*)this)->typeA::x’ from ‘std::shared_ptr<std::__cxx11::list<std::shared_ptr<typeB> > >’ to ‘std::shared_ptr<std::__cxx11::list<std::shared_ptr<const typeB> > >’|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
It seems as const shared_ptr<list<shared_ptr<typeB>>> and shared_ptr<const list<shared_ptr<typeB>>> work fine.
Is it possible to do return l as a complete const, like:
const shared_ptr<const list<shared_ptr<const typeB>>>
or at least like:
shared_ptr<list<shared_ptr<const typeB>>>
?
References instead of pointers is not an option. To declare l as shared_ptr<list<shared_ptr<const typeB>>> also is not a wanted solution.
EDIT: no 'int' anymore.
It seems as it is not possible exactly what I wanted, but the suggested solutions are good. Yes, copying pointers is acceptable.
My bad i didn't put typeB immediately. I am aware of some advantages of references over pointers, but I hoped there is some similar solution.
You can create a new list of const int's from your original list and return that:
std::shared_ptr<std::list<std::shared_ptr<const int>>> getList(){
return std::make_shared<std::list<std::shared_ptr<const int>>>(l->begin(), l->end());
}
If you want to prevent people from making changes to the returned list, make it const too:
std::shared_ptr<const std::list<std::shared_ptr<const T>>> getList(){
return std::make_shared<const std::list<std::shared_ptr<const T>>>(l->cbegin(), l->cend());
}
The shared pointer returned by this function does not point to the original list but to the newly created list.
An alternative may be to provide iterators that, when dereferenced, returns const T& (where T is the type you actually store). That way there will be no need to copy the whole list every time you want to go though it. Example:
#include <iostream>
#include <list>
#include <memory>
struct example {
int data;
example(int x) : data(x) {}
};
template <class T>
class typeA {
std::shared_ptr<std::list<std::shared_ptr<T>>> l = std::make_shared<std::list<std::shared_ptr<T>>>();
public:
template< class... Args >
void add( Args&&... args ) {
l->emplace_back(std::make_shared<T>(std::forward<Args>(args)...));
}
// a very basic iterator that can be extended as needed
struct const_iterator {
using uiterator = typename std::list<std::shared_ptr<T>>::const_iterator;
uiterator lit;
const_iterator(uiterator init) : lit(init) {}
const_iterator& operator++() { ++lit; return *this; }
const T& operator*() const { return *(*lit).get(); }
bool operator!=(const const_iterator& rhs) const { return lit != rhs.lit; }
};
const_iterator cbegin() const noexcept { return const_iterator(l->cbegin()); }
const_iterator cend() const noexcept { return const_iterator(l->cend()); }
auto begin() const noexcept { return cbegin(); }
auto end() const noexcept { return cend(); }
};
int main() {
typeA<example> apa;
apa.add(10);
apa.add(20);
apa.add(30);
for(auto& a : apa) {
// a.data = 5; // error: assignment of member ‘example::data’ in read-only object
std::cout << a.data << "\n";
}
}
When you convert a pointer-to-nonconst to a pointer-to-const, you have two pointers. Furthermore, a list of pointers-to-nonconst is a completely different type from a list of pointers-to-const.
Thus, if you want to return a pointer to a list of pointers-to-const, what you must have is a list of pointers-to-const. But you don't have such list. You have a list of pointers-to-nonconst and those list types are not interconvertible.
Of course, you could transform your pointers-to-nonconst into a list of pointers-to-const, but you must understand that it is a separate list. A pointer to former type cannot point to the latter.
So, here is an example to transform the list (I didn't test, may contain typos or mistakes):
list<shared_ptr<const int>> const_copy_of_list;
std::transform(l->begin(), l->end(), std::back_inserter(const_copy_of_list),
[](auto& ptr) {
return static_pointer_cast<const int>(ptr);
});
// or more simply as shown by Ted:
list<shared_ptr<const int>> const_copy_of_list(l->begin(), l->end());
Since we have created a completely new list, which cannot be pointed by l, it makes little sense to return a pointer. Let us return the list itself. The caller can wrap the list in shared ownership if the need it, but don't have to when it is against their needs:
list<shared_ptr<const int>> getConstCopyList() {
// ... the transorm above
return const_copy_of_list;
}
Note that while the list is separate, the pointers inside still point to the same integers.
As a side note, please consider whether shared ownership of an int object makes sense for your program - I'm assuming it is a simplification for the example.
Also reconsider whether "References instead of pointers is not an option" is a sensible requirement.
You problem squarely lies at
but I do not want to mix references and pointers. It is easier and cleaner to have just pointers.
What you are finding here is that statement is wrong. A list<TypeB> can bind a const list<TypeB> & reference, and none of the list's members will allow any modification of the TypeB objects.
class typeA {
std::vector<typeB> l;
public:
const std::vector<typeB> & getList() const { return l; };
};
If you really really must have const typeB, you could instead return a projection of l that has added const, but that wouldn't be a Container, but instead a Range (using the ranges library voted into C++20, see also its standalone implementation)
std::shared_ptr<const typeB> add_const(std::shared_ptr<typeB> ptr)
{
return { ptr, ptr.get() };
}
class typeA {
std::vector<std::shared_ptr<typeB>> l;
public:
auto getList() const { return l | std::ranges::transform(add_const); };
};
Another alternative is that you can wrap your std::shared_ptrs in something like std::experimental::propagate_const, and just directly return them.
What you have here is a VERY complex construct:
shared_ptr<list<shared_ptr<typeB>>> l;
This is three levels of indirection, of which two have reference counting lifetime management, and the third is a container (and not memory-contiguous at that).
Naturally, given this complex structure, it is not going to be easy to convert it to another type:
shared_ptr<list<shared_ptr<const typeB>>>
Notice that std::list<A> and std::list<const A> are two distinct types by design of standard library. When you want to pass around non-modifying handles to your containers, you are usually supposed to use const_iterators.
In your case there is a shared_ptr on top of the list, so you can't use iterators if you want that reference counting behavior.
At this point comes the question: do you REALLY want that behavior?
Are you expecting a situation where your typeA instance is destroyed, but you still have some other typeA instances with the same container?
Are you expecting a situation where all your typeA instances sharing the container are destroyed, but you still have some references to that container in other places of your runtime?
Are you expecting a situation where the container itself is destroyed, but you still have some references to some of the elements?
Do you have any reason at all to use std::list instead of more conventional containers to store shared pointers?
If you answer YES to all the bullet points, then to achieve your goal you'll probably have to design a new class that would behave as a holder for your shared_ptr<list<shared_ptr<typeB>>>, while only providing const access to the elements.
If, however, on one of the bullet points your answer is NO, consider redesigning the l type. I suggest starting with std::vector<typeB> and then only adding necessary modifications one by one.
The problem with templates is that for any
template <typename T>
class C { };
any two pairs C<TypeA> and C<TypeB> are totally unrelated classes – this is even the case if TypeA and TypeB only differ in const-ness.
So what you actually want to have is technically not possible. I won't present a new workaround for now, as there are already, but try to look a bit further: As denoted in comments already, you might be facing a XY problem.
Question is: What would a user do with such a list? She/he might be iterating over it – or access single elements. Then why not make your entire class look/behave like a list?
class typeA
{
// wondering pretty much why you need a shared pointer here at all!
// (instead of directly aggregating the list)
shared_ptr<list<shared_ptr<typeB>>> l;
public:
shared_ptr<list<shared_ptr<typeB>>>::const_iterator begin() { return l->begin(); }
shared_ptr<list<shared_ptr<typeB>>>::const_iterator end() { return l->end(); }
};
If you used a vector instead of a list, I'd yet provide an index operator:
shared_ptr<typeB /* const or not? */> operator[](size_t index);
Now one problem yet remains unsolved so far: The two const_iterators returned have an immutable shared pointer, but the pointee is still mutable!
This is a bit of trouble - you'll need to implement your own iterator class now:
class TypeA
{
public:
class iterator
{
std::list<std::shared_ptr<int>>::iterator i;
public:
// implementation as needed: operators, type traits, etc.
};
};
Have a look at std::iterator for a full example – be aware, though, that std::iterator is deprecated, so you'll need to implement the type-traits yourself.
The iterator tag to be used would be std::bidirectional_iterator_tag or random_access_iterator_tag (contiguous_iterator_tag with C++20), if you use a std::vector inside.
Now important is how you implement two of the needed operators:
std::shared_ptr<int const> TypeA::iterator::operator*()
{
return std::shared_ptr<int const>(*i);
}
std::shared_ptr<int const> TypeA::iterator::operator->()
{
return *this;
}
The other operators would just forward the operation to the internal iterators (increment, decrement if available, comparison, etc).
I do not claim this is the Holy Grail, the path you need to follow under all circumstances. But it is a valuable alternative worth to at least consider...

const access and non-const access

I have a class which internally owns a vector of foo
class bar {
private:
vector<Foo> foos_;
}
Now I want to design public access to this vector. I am thinking of two versions of the function:
Foo& getFoo(int index) {
// first do size checking, return ref
return foos[index];
}
and
const Foo& getFoo(int index) const {
// first do size checking, return const reference
return foos[index];
}
Any downside of this approach? One obvious downside is I copy the almost identical code simply twice. Is there a better way to do this?
----- edit -----
the second accessor forgets const, updated
Having both const and non-const accessors is somewhat common in C++. There is no language feature to combine the code for both--you really do need to write it twice.
By the way, you don't need to do bounds checking yourself, you can use foos_.at(index) instead of foos_[index] and then you'll have automatic bounds checking.

How to modify a private array without giving too much freedom?

I have a class named A which contains a private, dynamically-allocated array of class B objects. I have an array of pointers(?) to elements of the B array inside of A (the first array described) and I need a function which would help me modify this array (to actually let me get and point to those elements).
What would it be the best way to work with? Pointers, references?
One way I thought of would be to create a getter within A which returns the address of the array or of an element of the array, but I think that it gives too much freedom outside the class.
Thank you (and sorry for confusing you with my question) but I am pretty new to these things. Hopefully, you will understand better with this drawing:
If you really must use dynamic allocation, but wish to offer references to the Bs, you may do something like this:
struct B {};
struct A
{
B& operator[](size_t i) {
return *_bs[i];
}
const B& operator[](size_t i) const {
return *_bs[i];
}
std::size_t size() const {
return _bs.size();
}
private:
std::vector<std::unique_ptr<B>> _bs;
};
The ideal solution would be to be able, given an instance of A a and a handler to designate a unique instance of B owned by a, but without having total control over this B.
I'd suggest a simple index inside the array:
class A
{
Array _array;
public:
// ctr, operator=, ...
const B& operator[](std::size_t index) const { return _array[index]; }
B& operator[](std::size_t index) { return _array[index]; } // if necessary
};
Ou could also define an iterator for this array, or return a A::_array::const_iterator. The possibilities are infinite (kind of) and the best choice depends on your actual constrains.

How to access a container methods when the container is in a class as a private member?

I have a simple question. I have a class say A with a private member that is a stl container, for example a vector of ints. Is there a way to use its methods (e.g. size, resize, etc...) ? Does the classic "get function" suffice ?
Class A
{
private:
std::vector<int> v;
public:
std::vector<int> get_v() {return v;};
};
If yes, isn't a "get function" meant to only get the member and not to modify the member ?
Thank you very much
The normal thing to do here is to return a constant reference to the data member:
const std::vector<int>& get_v() const
{
return v;
}
Note that I've also made the function constant. This tells you that the function will not modify any data members in the class.
Currently, you are taking a deep copy of the vector, which is expensive in terms of performance and also detaches you from the original data.
If you want to call methods like resize (that change the vector) then you can also supply a non-constant version of the function (overloading on const) is allowed in C++):
std::vector<int>& get_v()
{
return v;
}
The compiler will call the const version if you have a const pointer (or reference) to an instance of A. Else if will call the non-const version.
That getter will return a copy of the internal vector. Any modifications you make will only affect the copy. That might be okay if you also provide a setter so that the modified copy can be passed back, so you could do something like this:
A a;
std::vector<int> v = a.get_v();
v.push_back(5);
a.set_v(v);
Alternatively, you can make the getter return a reference to the internal vector (so its return type would be std::vector<int>&, then you can modify it from outside.
Your get method returns a copy of the data member. So such a method does not allow to modify the original vector,
If you want to provide an access to elements of the vector you can either overload operator[] or write a get method with a parameter.
For example
Class A
{
private:
std::vector<int> v;
public:
int operator []( std::vector<int>::size_type n ) const { return v[n]; }
int & operator []( std::vector<int>::size_type n ) { return v[n]; }
};
or you can write get methods that do the same
Class A
{
private:
std::vector<int> v;
public:
int get( std::vector<int>::size_type n ) const { return v[n]; }
int & get( std::vector<int>::size_type n ) { return v[n]; }
};
As for the idea to use a copy of the vector or a reference to it that to get its size then it is not a good idea. It is better to provide a method that returns the size of the vector. In this case the user interface will not depend on the type of the container.

Const vector of non-const objects

In defining a function in an interface :
virtual void ModifyPreComputedCoeffs ( std::vector < IndexCoeffPair_t > & model_ ) = 0;
we want to specify that the vector model_ should not be altered in the sense push_back etc operations should not be done on the vector, but the IndexCoeffPair_t struct objects in the model_ could be changed.
How should we specify that ?
virtual void ModifyPreComputedCoeffs ( const std::vector < IndexCoeffPair_t > & model_ ) = 0;
does not work I think.
Rather than passing the vector into the function, do what the standard library does and pass a pair of iterators instead.
virtual void ModifyPreComputedCoeffs ( std::vector < IndexCoeffPair_t >::iterator & model_begin, std::vector < IndexCoeffPair_t >::iterator & model_end )
The C++ const-correctness concept is IMO way overrated. What you just discovered is one of the big limitations it has: it doesn't scale by composition.
To be able to create a const vector of non-const objects you need to implement your own vector type. Note that for example even the standard library had to introduce new types for const_iterators.
My suggestion is to use const-correctness where you are forced to, and not everywhere you can. In theory const correctness should help programmers, but comes at a very high cost because of the syntax and is very primitive (just one bit, doesn't scale by composition, even requires code duplication).
Also in my experience this alleged big help is not really that big... most of the errors it catches are related to the const-correctness machinery itself and not to program logic.
Ever wondered why most languages (including ones designed after C++) didn't implement this idea?
This is likely to be in C++14 as std::dynarray.
Actually if the size is fixed at compile time you can use std::array. But it's probably more use for things like embedded programming, buffers, matrices and so on as often you don't know the desired size until runtime or you want it to be configurable.
If you are able to modify IndexCoeffPair_t, you could add some const member functions and use them to change some of its members by making the members mutable using the mutable keyword. This is kind of a hack though, since you would now be able to change the contents of any const IndexCoeffPair_t.
Example:
class IndexCoeffPair_t {
public:
void changeX(int newVal) const {
x = newVal;
}
private:
mutable int x;
};
Here's a generic version of MahlerFive's answer:
template<typename T>
class Mutable {
mutable T m_val;
public:
constexpr Mutable(T const& val) : m_val(val) { }
constexpr Mutable(T&& val) : m_val(val) { }
// note: all member functions are `const`
constexpr Mutable const& operator=(T const& val) const {
m_val = val;
return *this;
}
constexpr Mutable const& operator=(T&& val) const {
m_val = val;
return *this;
}
constexpr operator T&() const {
return m_val;
}
};
You can then use std::vector<Mutable<T>> const in your code, which will mostly behave as intended.
You can try to create const std::vector<YouType*>. Then you can't change the vector but you can change objects inside vector.
But be accurate because you will modify original objects not copies.
Use smart pointers or raw pointers depends on your use cases: you have owning vector or just vector of observers.