I want to provide different levels of const 'access' to my data. For example, depending on whether the pointer or data should be modified. So this is what I came up with:
class MyClass
{
int n;
int* ptr_to_data;
int* const const_ptr_to_data;
const int * ptr_to_const_data;
public:
MyClass(int nn)
: n(nn),
ptr_to_data(&n),
const_ptr_to_data(ptr_to_data),
ptr_to_const_data(ptr_to_data)
{
}
~MyClass() { }
int& get_data()
{
return *const_ptr_to_data;
}
const int& get_data() const
{
return *ptr_to_const_data;
}
};
The goal here is to avoid programmer errors by restricting as much access as possible. Is this a good approach, how to make it better?
You have the correct approach with the two get_data functions, but all the pointers just make the code harder to maintain. Just this is sufficient:
int& get_data() { return n; }
const int& get_data() const { return n; }
The pointers you store don't help with the access problem, as you'll see, but they will refer to data in the wrong instance when you copy an object, unless you take charge of copying. And the top level const prevents assignment for class instances. I.e. the pointers are problematic, and do not contribute any advantage.
Instead do this:
int data() const
{
return n_;
}
void set_data( int const value )
{
n_ = value;
}
Or you might do as in the standard library and name also the setter just data, but the imperative form is more readable in the calling code.
A key feature of this approach is not pass out a pointer or reference to non-const data member.
Because by passing out unrestricted reference or pointer you lose all control over changes to that data member, in particular with respect to maintaining a class invariant, possibly other related values, imposing range restrictions, checking when changes are made, and so on.
Related
In the library I am designing, I sometimes need read-access to large member variables of classes. Because of their size I don't want to make a getter that returns by copying the member. I don't want them to be modifiable from the outside, so I can't have them public or return references to them. So I thought I would use a "reader":
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
const double& readD() const { return d_; }
private:
double d_;
};
(this is not really meant for doubles)
But here somebody could const_cast the reference and access the data directly. Even without assuming malicious intent, somebody could safe a reference to the data-member and keep it around after the original object has gone out of scope. I know const references can keep a temporary viable, but that doesn't remove the const_cast-problem.
So I came up with a workaround:
#include <iostream>
template<class T>
class SafeMemberReference
{
public:
using type = T;
SafeMemberReference(const T& t) :t(t) {}
explicit SafeMemberReference(T&& t) = delete;
operator const T& () && {return t; }
T get() && {return t; }
private:
const T& t;
};
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
SafeMemberReference<double> readD() const { return d_; }
private:
double d_;
};
int main()
{
TestClass foo(1.2);
// temporary from read can be used as temporary in expressions
std::cout << foo.readD() << std::endl;
// temporary can be used to copy from
auto x = foo.readD().get();
// lvalue can not be used, so a possible dangling reference is no problem
auto ref = foo.readD();
//std::cout << ref << std::endl;
}
I have several questions to this:
Q1) How necessary is this from an efficiency POV? the largest objects I am returning are dense complex matrices with dimensions of maybe 1000x1000. These copies may happen frequently
Q2) Are my concerns about returning by const& valid?
Q3) Does this seem like a good solution? which drawbacks does it have?
Any solution that attempts to fight the language itself is not a good solution.
They should get a wrap on the knuckles if they used a const_cast in that way: the behaviour on trying to change an object via a const_cast on an object that was originally declared as const is undefined. Even if you manage to conjure up a solution to prevent that, a hostile programmer could still take the address of your object, offset that address (using unsigned char* pointer arithmetic) and modify a data member through that pointer!
So if I were you I wouldn't fight the language. Return a const reference if I were you, as per your original suggestion.
Code static analysis tools / compiler warnings / code reviews / human resource departments will help you keep other collaborative programmers on the straight and narrow.
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.
I have a "sum" class which holds two references to existing ints (say). I want to create a "copy" method which deep copies the ints. I thought I would never have to manually delete objects in my code, thanks to smart pointers, but I had to in this solution. Moreover, it is too complicated for a so trivial task (which I need to repeat for several classes). Is there a more straightforward solution?
Note: I don't want to add a bool member (flag) to each objects to determine if the ints must be deleted (in my case, it's not a better overhead than the std::set check overhead in the destructor)
#include <set>
struct sum {
const int &a, &b;
static std::set<const int*> allocated_ints;
sum(const int& a, const int&b): a(a), b(b) {}
sum copy() const {
sum res(*new const int(a), *new const int(b));
allocated_ints.insert(&res.a);
allocated_ints.insert(&res.b);
return res;
}
~sum() {
if (allocated_ints.count(&this->a)) {
delete &this->a;
delete &this->b;
allocated_ints.erase(&this->a);
allocated_ints.erase(&this->b);
}
}
};
std::set<const int*> sum::allocated_ints;
What's the point of a "deep" copy of constants? The constants are going to have the same value no matter what! So just copy (i.e. alias) the const-references:
struct Foo
{
const int & n;
Foo(const int & m) : n(m) { }
Foo(const Foo & rhs) : n(rhs.n) { }
Foo copy() const { Foo f(*this); /* ... */ return f; }
// ...
};
If you're worried about dangling references when returning a copy from a function with a reference to a local variable, then don't make the class have const references, but copies. That way you naturally give your class the copy semantics that you seem to be after anyway.
If you were thinking that you could make a hybrid which is either non-owning or becomes owning depending on how you use it, then I'd say that's bad design that you should avoid. Decide whether your class has ownership over the data or not and then roll with it.
I think you're mixing-up two incompatible concepts.
If you initialize by reference you should refer to existing object whose lifetime is already defined and should be longer than your objects.
If you want to create a copy of your object, since it refers to something, your copy will also refer to that something.
If you want to own yourself dynamic supplied objects, you should use pointers for that, and acquire them as pointers (and delete them on destruction). A copy can then deep-create copies of the pointed objects (or can share them using reference counting or shared_ptr).
You are -in fact- making up a mixing of the two things, resulting in possible problems: think to:
int main()
{
const int x=5; //whatever it is
Foo foo(x);
// ...
} //danger here! ~Foo() will delete x
The references are not deep copied, because they point to an object. Therefore, your code fixed should look like this :
struct sum {
const int &a, &b;
sum(const int& a, const int&b): a(a), b(b) {}
sum copy() const {
sum res(a,b);
return res;
}
~sum() {
}
};
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.
I have const-overloaded methods in my class:
class A{
public:
typedef int data[10];
data& operator[](int index);
const data& operator[](int index) const;
}
This class is implementing copy-on-write for its internal data. I figured that since i am allowing to access data directly, i must create copy of shared data (if it is shared obviously) on every use of operator[], but not operator[] const. However, even if code is using operator[] for reading data, but object itself is not declared as const, it will still cause creating copy, as operator[] will be used. Is there any syntax that would let me allow to choose which of those operators i am calling?
Yes: const_cast<A const&>(anAObj)[5].
Why does the operator return a data& rather than an int& that references a single item?
That being said your options include:
Always do the copy even if it turns out to be unneeded.
Use the operator for read or write, and a named method (GetRef for example) for the other one.
Cast the object to const at the use point: static_cast<const A&>(obj)[index].
You need to create a Proxy class to substitute the return value of both methods. Then, in the Proxy class you can handle reads and writes appropriately. Here's code that should compile to demonstrate the idea (working with an array of unsigned integers):
typedef unsigned int UINT;
class A {
class Proxy {
public:
Proxy(const UINT &number): _number(number) {}
const Proxy &operator=(const Proxy &obj) {
cout << "Writting...\n";
_number = obj._number;
return *this;
}
operator const UINT &() const {
cout << "Reading...\n";
return _number;
}
private:
UINT _number;
};
public:
A(UINT *array): _array(array) {}
Proxy operator[](int index) {
return _array[index];
}
const Proxy operator[](int index) const {
return _array[index];
}
private:
UINT *_array;
};
int main(int argc, char** argv) {
UINT myArray[] = {0, 1, 2, 3, 4};
A a(myArray); // Normal A object
UINT num1 = a[1]; // Reading fine
a[1] = num1; // Writting fine
const A ca(myArray); // Constant A object
UINT num2 = ca[1]; // Reading fine
ca[1] = num2; // Writting NOT fine (compilation error)
return 0;
}
implement data& at(size_t i); and const data& cat(size_t); functions, so you can invoke cat() on non-const objects to enforce constness of returned data.
No, unless you cast the reference/pointer to a constant. Or create a constant copy.
A a;
const_cast<const A &> (a)[0] /*.foo ()*/;
If you are implementing copy-on-write, then you should not define those subscription operators at all. A client that calls the const version of the subscription method for an object x is not allowed to subsequently use that reference to modify a component of x, but the reference should still reflect the changes that other clients make to (that component of) x. But his is not going to happen with a copy-on-write strategy, since the change will happen in a different copy than the reference points to.
Also two clients that call the non-const subscription operator with the same index should get (modifyable) references to the same data object; but they won't, because two seperate copies will have been made when the subscription operators were called.
Instead you could have a subscription method that returns data by value. This avoids creating the illusion of obtaining an alias for a component of x (which as I argued must be illusory when copy-on-write is implemented). You could also provide a single method that modifies a component of x (rather than splitting this operation into a subscription followed by assignment to the reference so obtained), which would either internally make a copy, or just modify a component in place if this copy was already made. This would hide from clients the use of a copy-on-write implementation.
More generally, exposing methods that return references to internal objects, whether const or not, is exposing the implementation detail that such objects exist at all. This limits the freedom of changing the implementation later, for instance to compress the data or to store it elsewhere than in memory.