Related
I am currently rewriting C code into C++ code. While doing that I am replacing structs with classes. That means that some of the variables go from public to private. Now during the transition phase I want to do some error checking by compiling the program sometimes and running it. Thus I intended to have public and private variables at the same time, which are linked, i.e. when I write something into the private variable, the public variable also changes. Nevertheless I only want to write to the private variables by using separate functions, i.e. having the public variables as read-only variables. My current approach is:
#include <iostream>
#include <stdio.h>
class test_vec
{
private:
int x, y;
int data[2];
public:
int x_ext, y_ext;
int data_ext[2];
test_vec(int x, int y)
{
this->x = x;
this->y = y;
this->x_ext = this->x;
this->y_ext = this->y;
}
~test_vec()
{}
void set_x(int x)
{
this->x = x;
}
void set_y(int y)
{
this->y = y;
}
};
int main(void)
{
std::cout << "Hello World\n";
test_vec test(1, 2);
printf("test has the properties (%d, %d)\n", test.x_ext, test.y_ext);//So far, so good
test.set_x(4);
test.set_y(10);
printf("test has the properties (%d, %d)\n", test.x_ext, test.y_ext);//Not updated!
return 0;
}
How can I change the links between the variables? At the moment I already have two pointers copied into each other, but how can I "lock" the external variable onto the internal variable?
Not sure if it's a good design pattern since inline getters are fast but you could create constant references to your private variables:
class test_vec
{
private:
int x, y;
int data[2];
public:
const int &x_ext, &y_ext;
int data_ext[2];
// you have to initialize the references before constructor body
// references cannot be let uninitialized
test_vec(int x, int y) : x_ext(this->x), y_ext(this->y)
{
this->x = x;
this->y = y;
}
~test_vec()
{}
inline void set_x(int x)
{
this->x = x;
}
inline void set_y(int y)
{
this->y = y;
}
};
when x or y changes x_ext and y_ext follow:
Hello World
test has the properties (1, 2)
test has the properties (4, 10)
Bonus: constant references cannot be modified. That's the closest thing of a read property that you got here :)
If you don't want that restriction, just remove the const qualifier, but since you're encouraging encapsuation now that you have C++ I would let it as is and let the writers hit the wall on that (not to mention a good sed/regex replacement could refactor all your writes automatically)
You can use references for these purposes.
Say you have this setup:
class myclass{
public:
myclass(int pa, float pb);
int get_a() const {return a;}
float get_b() const {return b;}
void set_a(int v) {a=v;}
void set_b(float v) {b=v;}
private:
//These are the real values, private
int a;
float b;
public:
//These are the references, public
int& ref_to_a;
float& ref_to_b;
}
myclass::myclass(int pa, float pb)
:a(pa), b(pb), ref_to_a(a), ref_to_b(b)
{
}
You can go like this:
myclass c(33, 12.3f);
c.set_a(12);
c.set_b(111.1f);
//This...
std::cout<<c.ref_to_a<<" "<<c.ref_to_b<<std::endl;
//Should be the same as this...
std::cout<<c.get_a()<<" "<<c.get_b()<<std::endl;
Notice the access settings: the references are public, meaning you can write and read from them. If you want them to be read only you can play with the constness.
I wouldn't bother. Just make the members public for the moment, and when you have fixed all the external references, make them private.
class test_vec
{
public: // For now. Will become private later
int x, y;
public: // For now.
int data[2];
public: // For ever
test_vec(int x, int y)
: x(x), y(y) // Prefer initialization to assignment.
{
}
~test_vec()
{}
void set_x(int x)
{
this->x = x;
}
void set_y(int y)
{
this->y = y;
}
int get_x() const { return x; } // etc
};
If you really wanted to, you could make x_ext be a reference to const - but it's much more idiomatic in C++ to make getters be functions.
class test_vec
{
private:
int x, y;
int data[2];
public:
int const& x_ext;
int const& y_ext;
test_vec(int x_, int y_)
: x(x_), y(y_)
, x_ext(x), y_ext(y) // You *have* to use initialization here.
{
}
~test_vec()
{}
void set_x(int x)
{
this->x = x;
}
void set_y(int y)
{
this->y = y;
}
};
So I have two different structs (a & b) with same variables and an overloaded = operator in struct b to convert a to b.
I want to be able to simple assign a vector of a to a vector b, but compiler gives me an error:
main.cpp|61|error C2679: binary '=' : no operator found which takes a right-hand operand of type 'std::vector<_Ty>' (or there is no acceptable conversion)|
I assumed that I already had the overloaded = operator and that it would simply iterate over vector a and use that = operator for each instance. How would I do this?
Here is the code:
#include <iostream>
#include <vector>
using namespace std;
struct a
{
int x, y;
a() {}
a(int _x, int _y)
{
x = _x;
y = _y;
}
};
struct b
{
int x, y;
b(){}
b(int _x, int _y)
{
x = _x;
y = _y;
}
b& operator=(const a& _a)
{
x = _a.x;
y = _a.y;
return *this;
}
};
int main()
{
a a_test(1,2);
std::vector<a> a_vec;
std::vector<b> b_vec;
for(int i = 0; i <10; i++)
{
a_vec.push_back(a_test);
}
/*
for(int i = 0; i<a_vec.size(); i++)
{
b_vec.push_back(a_vec[i]);
}
*/
b_vec = a_vec;
return 0;
}
The problem is that your operator= only works on individual elements, not on whole vectors.
You need to define a constructor that converts an A into a B.
Then you can use std::vector::assign rather than std::vector::operator=.
#include <iostream>
#include <vector>
using namespace std;
struct A
{
int x, y;
A(): x(0), y(0) {}
A(int x, int y): x(x), y(y) {}
};
struct B
{
int x, y;
B(): x(0), y(0) {}
B(int x, int y): x(x), y(y) {}
// need to construct B from A
B(const A& a): x(a.x), y(a.y) {}
B& operator=(const A& a)
{
x = a.x;
y = a.y;
return *this;
}
};
int main()
{
A a_test(1,2);
std::vector<A> a_vec;
std::vector<B> b_vec;
for(int i = 0; i <10; i++)
{
a_vec.push_back(a_test);
}
// b_vec = a_vec; // not like this
b_vec.assign(a_vec.begin(), a_vec.end()); // like this
return 0;
}
NOTE: I changed some of your names because the C++ standard says we should not begin variable names with the underscore '_'.
Even though a and b look identical, the compiler sees them as different types, even if a is convertible to b (or vice versa). Hence, vector<a> and vector<b> are completely unrelated, so you cannot assign one to the other, and the whole assignment fails to compile.
You can use an algorithm like std::copy,
std::copy(a_vec.begin(), a_vec.end(), std::back_inserter(b_vec));
I'm coming to C++ from C# and const-correctness is still new to me. In C# I could declare a property like this:
class Type
{
public readonly int x;
public Type(int y)
{
x = y;
}
}
This would ensure that x was only set during initialization. I would like to do something similar in C++. The best I can come up with though is:
class Type
{
private:
int _x;
public:
Type(int y) { _x = y; }
int get_x() { return _x; }
};
Is there a better way to do this? Even better: Can I do this with a struct? The type I have in mind is really just a collection of data, with no logic, so a struct would be better if I could guarantee that its values are set only during initialization.
There is a const modifier:
class Type
{
private:
const int _x;
int j;
public:
Type(int y):_x(y) { j = 5; }
int get_x() { return _x; }
// disable changing the object through assignment
Type& operator=(const Type&) = delete;
};
Note that you need to initialize constant in the constructor initialization list. Other variables you can also initialize in the constructor body.
About your second question, yes, you can do something like this:
struct Type
{
const int x;
const int y;
Type(int vx, int vy): x(vx), y(vy){}
// disable changing the object through assignment
Type& operator=(const Type&) = delete;
};
Rather than a collection of constants, you could have a constant collection. The property of being constant seems to pertain to your use case, not the data model itself. Like so:
struct extent { int width; int height; };
const extent e { 20, 30 };
It's possible to have specifically constant data members of a class, but then you need to write a constructor to initialize it:
struct Foo
{
const int x;
int & y;
int z;
Foo(int a, int & b) : x(a + b), y(b), z(b - a) { }
};
(The example also shows another type of data member that needs to be initialized: references.)
Of course, structs and classes are the same thing.
You can initialize class const members with constructor. If you need add some other logic in constructor, but in .cpp file not in .h, you can create a private method and call it in constructor.
File.h
class Example
{
private:
const int constantMember1;
const int constantMember2;
const int constantMember3;
void Init();
public:
Example(int a, int b) :constantMember1(a), constantMember2(b), constantMember3(a + b) {
//Initialization
Init();
};
};
File.cpp
void Init()
{
//Some Logic intialization
}
This is not exactly answering the question asked, but if you wanted to have the simplicity of directly accessing member variables in a struct without getters, but wanted to ensure that nobody could modify the values, you could do something like this:
#include <iostream>
using namespace std;
class TypeFriend;
struct Type
{
const int &x;
const int y;
Type (int vx, int vy):x (_x), y (vy), _x (vx)
{
}
private:
friend class TypeFriend;
int _x;
};
struct TypeFriend
{
TypeFriend (Type & t):_t (t)
{
}
void setX (int newX)
{
_t._x = newX;
}
private:
Type & _t;
};
int main ()
{
Type t (1, 2);
TypeFriend tf (t);
cout << t.x << "," << t.y << endl;
// t.x = 6; // error: assignment of read-only location ‘t.Type::x’
// cout<<t.x << ","<<t.y<<endl;
tf.setX (5);
cout << t.x << "," << t.y << endl;
return 0;
}
The result of running this is:
1,2
5,2
Type::x cannot be modified externally, so it is read-only, but via TypeFriend it can be changed. This can be useful if you wanted to expose a simple interface of direct member access for reading, but wanted to restrict how those members could be changed.
I use the named constructor idiom to create objects, because I have lots of calls with identical parameters but the object shall be created differently.
The C++ FAQ tell us how to do this. It also tells us how to force objects being heap allocated. Yet it really fails to tell us how to use the named constructor idiom with the new operator.
Because new requires a constructor to be called we cannot directly call named constructors. So I found two workarounds to this problem:
I create an additional copy constructor and hope that optimizing compilers won't create a temporary object.
class point_t {
int X,Y;
point_t(int x, int y) : X(x), Y(y) { }
public:
point_t(const point_t &x) : X(x.X), Y(x.Y) { }
static point_t carthesian(int x, int y) { return point_t(x,y); }
static point_t polar(float radius, float angle) {
return point_t(radius*std::cos(angle), radius*std::sin(angle));
}
void add(int x, int y) { X += x; Y += y; }
};
int main(int argc, char **argv) {
/* XXX: hope that compiler doesn't create a temporary */
point_t *x = new point_t(point_t::carthesian(1,2));
x->add(1,2);
}
The other version is to create separate named constructors. Because function overloading doesn't work on return type I use two different names, which is ugly.
class point_t {
int X,Y;
point_t(int x, int y) : X(x), Y(y) { }
public:
/* XXX: function overloading doesn't work on return types */
static point_t carthesian(int x, int y) { return point_t(x,y); }
static point_t *carthesian_heap(int x, int y) { return new point_t(x,y); }
void add(int x, int y) { X += x; Y += y; }
};
int main(int argc, char **argv) {
point_t *x = point_t::carthesian_heap(1,2);
x->add(1,2);
}
Is there a prettier version that is equal to the example code?
You can avoid named constructor idiom for this completely, and do it using an additonal dummy enum parameter to select the constructor.
enum Carthesian {carthesian};
enum Polar {polar};
class point_t {
int X,Y;
public:
point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
point_t(Carthesian, int x, int y) :X(x),Y(y){}
point_t(Polar, float radius, float angle)
: X (radius*std::cos(angle)), Y(radius*std::sin(angle)) {}
void add(int x, int y) { X += x; Y += y; }
};
int main(int argc, char **argv) {
point_t *x = new point_t(carthesian,1,2);
point_t *y = new point_t(polar,0,3);
x->add(1,2);
}
It is simple, portable, and the only overhead you will see is for the passing of the dummy enum values. In the rare case this overhead is too high for you it can be eliminated by wrapping a function call even when the construction itself is not inlined, as follows:
enum Carthesian {carthesian};
enum Polar {polar};
class point_t {
int X,Y;
void initCarthesian(int x, int y); // may be long, not inlined
void initPolar(float radius, float angle);
public:
point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
point_t(Carthesian, int x, int y)
{initCarthesian(x,y);} // this is short and inlined
point_t(Polar, float radius, float angle) {initPolar(radius, angle);}
void add(int x, int y) { X += x; Y += y; }
};
Another approach is to use a derived class for construction. When using inner classes, it leads into quite a nice syntax I think:
class point_t {
int X,Y;
public:
struct carthesian;
struct polar;
point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
void add(int x, int y) { X += x; Y += y; }
};
struct point_t::carthesian: public point_t
{
carthesian(int x, int y):point_t(x,y){}
};
struct point_t::polar: public point_t
{
polar(float radius, float angle):point_t(radius*std::cos(angle),radius*std::sin(angle)){}
};
int main(int argc, char **argv) {
point_t *x = new point_t::carthesian(1,2);
point_t *y = new point_t::polar(0,3);
x->add(1,2);
return 0;
}
You could write :
point_t *x = new point_t(point_t::carthesian(1,2));
It first calls carthesian() and then the copy-constructor.
Or, is there any problem in it? Perhaps, a bit slow?
By the way, there is one clear advantage in this code: the programmer can clearly see the new operator in his code (where he is using point_t written by someone else), so you can assume that its his responsibility to call delete once he is done with x.
Is this really a problem? In my experience classes tend to be either dynamically allocated most of the time or seldom, if at all. Classes that represent values, such as your point_t class here, belong to the second category, while classes that represent entities (i.e. something with identity) belong to the first one.
So my suggestion is to chose what you think is the best approach for each class and only provide that. Note that you could always return a small directly allocated object which has a private pointer to a larger one, as in the Handle-Body idiom.
On the other hand, other answers show how you may disambiguate among constructors that take arguments of the same number and types. In this line of thought, one alternative approach is to introduce specific types for the arguments as follows:
class radius_t {
float R;
public:
explicit radius_t(float r) : R(r) {}
operator float() const { return R; }
};
class angle_t {
float A;
public:
explicit angle_t(float a) : A(a) {}
operator float() const { return A; }
};
class point_t {
float X,Y;
public:
point_t(float x, float y) : X(x), Y(y) { }
point_t(radius_t radius, angle_t angle) :
X(radius*std::cos(angle)), Y((radius*std::sin(angle)) {
}
void add(int x, int y) { X += x; Y += y; }
};
int main(int argc, char **argv) {
point_t *x = new point_t(radius_t(1),angle_t(2));
x->add(1,2);
}
One approach that I haven't seen is overloading the constructor making the heap allocation use the last argument as an out one (Granted that the second function is not technically a constructor, it doesn't return an instance). The result would be something like (taken as base your second code fragment):
class point_t {
int X,Y;
point_t(int x, int y) : X(x), Y(y) { }
public:
/* XXX: function overloading doesn't work on return types */
static point_t carthesian(const int x, const int y) { return point_t(x,y); }
static void carthesian(const int x, const int y, point_t * & point) { point = new point_t(x,y); }
void add(int x, int y) { X += x; Y += y; }
void add(const point_t & point) { this->X += point.x; this->Y += point.y; }
};
int main(int argc, char **argv) {
point_t p1 = point_t::carthesion(1, 2);
point_t * p2;
point_t::carthesian(1, 2, p2);
p2->add(p1);
}
Can think of template allocator:
template<typename T>
struct Allocator : T
{
template<typename A1, typename A2>
Allocator(A1 a1, A2 a2) : T(a1, a2) {}
};
class point_t {
//...
template<typename T> friend struct Allocator;
};
int main(int argc, char **argv) {
point_t *x = new Allocator<point_t>(1,2);
x->add(1,2);
}
Now Allocator is friend of point_t. So it can access its private constructor. Also, you can add few more constructors like <A1, A2> inside Allocator to make it more generalized. Advantages are:
It doesn't look verbose.
You don't have to worry about compiler optimizations
The friendship is not exploited as, Allocator is a template
and we use it solely for heap allocation
Demo.
I just asked this question and the good answers mentioned using an initialization list. So I looked it up in many various places. It was often said that one can use an initialization list to select which constructor to use.
class First
{private:
int a, b, c;
public:
First(int x);
First(int x, int y);
}
First::First(int x, int y, int z = 0)
{ /* this is a constructor that will take two or three int arguements. */ }
First::First(int x, int y = 0, int z = 0)
{ /* and this will be called if one arguement is given */ }
I thought all assignments should be avoided, so how would I write the initializer lists for these two constructors?
I'm not quite sure I follow. As it stands, by providing an argument to x and y (and therefore z), both constructors will be available to call, resulting in ambiguity.
I think what you're looking for is:
class First
{
public:
First(int x);
First(int x, int y, int z = 0);
};
// invoked as First f(1);
First::First(int x) :
a(x), b(0), c(0)
{}
// invoked as First f(1, 2); or First f(1, 2, 3);
First::First(int x, int y, int z) :
a(x), b(y), c(z)
{}
Ctor initialization list isn't for the purpose of choosing which ver of ctor will be chosen.
This is not how things work. If you want to use default arguments, use only one constructof, declare it with default arguments, and define it (without redefining them).
class First
{
private:
int a, b, c;
public:
First(int x, int y = 0, int z = 0);
};
First::First(int x, int y, int z)
{ /*...*/ }
Considering your question, i am not sure you know what an initialisation list is...
I think you mean this:
class First
{
private:
int a, b, c;
public:
First(int x);
First(int x, int y);
}
class Second
{
private:
Second(int x) : First(x) { }
Second(int x, int y) : First(x, y) { }
}
Maybe?