Faking constness when method is const from the outside (example: caching) - c++

Consider the following example:
#include <iostream>
using std::endl;
using std::cout;
class my_class {
private:
int _expensive_count_operation() const {
return 10;
}
bool cached;
int last_count;
public:
my_class() { cached = false; }
int get_count() {
if (cached) {
cout << "Got count from cache." << endl;
return last_count;
}
cout << "Computing count." << endl;
last_count = _expensive_count_operation();
cached = true;
return last_count;
}
int get_const_count() const {
my_class* _this = const_cast<my_class*>(this);
if (cached) {
cout << "Got count from cache." << endl;
return last_count;
}
cout << "Computing count." << endl;
_this->last_count = _expensive_count_operation();
_this->cached = true;
return last_count;
}
};
int main() {
my_class my_object1,my_object2;
int count;
count = my_object1.get_count();
cout << "Count: " << count << endl;
count = my_object1.get_count();
cout << "Count: " << count << endl;
count = my_object2.get_const_count();
cout << "Count: " << count << endl;
count = my_object2.get_const_count();
cout << "Count: " << count << endl;
}
The use of const_cast in the get_const_count method allows you to keep the method const. Now the question is what are the dangers associated with this? And how do they compare to the dangers associated with loosing the ability to use const instances (or pointers to them) of this class? And, even better, is there a better approach?
I often find myself in this situation where I have written code that I then want to optimize using caching. The problem is that I then have to remove the const declarations and propagate that change through the rest of the code base.

I would prefer to mark all cache related attributes as mutable. The meaning of const should be that the observable state is not affected by those methods (which is what your cache should do anyway). Keep in mind that all const-methods should also be thread-safe.
For further information I recommend the blog of Herb Sutter - he wrote some stuff about const-correctness since c++11. Here is also a direct Link to a related video from Herb.

It is ONLY valid to use const_cast to remove const if the original object was NOT const. If that is always the case, it should be no problem to do this.
Although I would prefer to set last_count and cached to mutable... Because you don't really want to accidentally modify any of the other parts of the object - but you DO want to modify those in the const member function.

Related

C++ use a class in a class (in a class) and call their functions

I have the following problem: I wanted to redo a project from good old C to C++ and make everything class(y) :) and keep it scalable from the beginning.
It is a simulation of cells (being part of a swarm) on a grid, so I decided the following structure:
class Simulation has an instance of
class Grid has an instance of
class Swarm has an instance of
class Cell
I defined the classes in separate header files. Then I need, of course, to be able to call functions in grid, swarm and cell as well. I wanted to do it straight forward:
Simulation mysim;
mysim.get_grid(0).any_function_here();
with the grid as return parameter
Grid Sim::get_grid(int grid_no)
{
std::cout << "sim.get_grid(" << grid_no << ") called." << std::endl;
if (grid_no <= amount_of_grids)
return this->test;//##//this->gridlist[grid_no];
else
std::cout << "you have not created this grid number yet" << std::endl;
Grid dummy;
return dummy;
}
It calls the function and works as long as no changes in the grid are made. These seem to be lost in space. Probably a pointer error, but I cannot find an error, since exactly the same code is working for the Simulation class...
More source:
int Grid::create_swarm(std::string name)
{
Swarm new_swarm;
new_swarm.set_name("Protoswarm");
swarmlist.push_back(new_swarm);
this->amount_of_swarms ++;
std::cout << "amount_of_swarms = " << amount_of_swarms << std::endl;
return 0;
}
Swarm Grid::get_swarm(int swarm_no)
{
std::cout << "grid.get_swarm(" << swarm_no << ") called." << std::endl;
if (swarm_no <= amount_of_swarms)
return swarmlist[swarm_no];
else
std::cout << "oh oh - you have not this swarm in here..." << std::endl;
Swarm dummy;
return dummy;
}
I can call the create_swarm function as often as I want, but the swarms do never appear and the counter does not raise in that grid, just temporarily as long as the funtion is in there. Am I missing something? Is it really just a pointer error? Why does this code work if I call it like this:
Grid newgrid;
newgrid.create_swarm();
A quickly c&p'ed MWE
#include <iostream>
#include <string>
#include <vector>
class Sim
{
public:
Sim();
virtual ~Sim();
Grid get_grid(int grid_no);
protected:
private:
std::vector<Grid> gridlist;
int amount_of_grids = -1;
};
class Grid
{
public:
Grid();
virtual ~Grid();
int set_size(int x, int y);
int create_swarm(std::string name);
Swarm get_swarm(int swarm_no);
void print_swarms();
protected:
private:
std::vector<Swarm> swarmlist;
int amount_of_swarms = -1;
/*static const*/ int size_x;
/*static const*/ int size_y;
std::vector<std::vector<Field>> fields;
std::string gridname;
};
Grid Sim::get_grid(int grid_no)
{
std::cout << "sim.get_grid(" << grid_no << ") called." << std::endl;
if (grid_no <= amount_of_grids)
return this->gridlist[grid_no];
else
std::cout << "you have not created this grid number yet" << std::endl;
Grid dummy;
return dummy;
}
int Grid::create_swarm(std::string name)
{
Swarm new_swarm;
new_swarm.set_name("Protoswarm");
swarmlist.push_back(new_swarm);
this->amount_of_swarms ++;
std::cout << "amount_of_swarms = " << amount_of_swarms << std::endl;
return 0;
}
Swarm Grid::get_swarm(int swarm_no)
{
std::cout << "grid.get_swarm(" << swarm_no << ") called." << std::endl;
if (swarm_no <= amount_of_swarms)
return swarmlist[swarm_no];
else
std::cout << "oh oh - you have not this swarm in here..." << std::endl;
Swarm dummy;
return dummy;
}
using namespace std;
int main(int argc, char* argv[])
{
Sim mysim;
mysim.create_grid();
mysim.get_grid(0).create_swarm("Alpha-Swarm");
mysim.get_grid(0).create_swarm("Betaa-Swarm"); //doesn't work
Grid newgrid;
newgrid.create_swarm("Gamma-Swarm");
newgrid.create_swarm("Delta-Swarm"); // works, but is not needed.
return 0;
}
Grid Sim::get_grid(int grid_no) {...}
You are returning by value, not by reference. That means that what you are returning is a copy of your actual member. In your case, however, you want to be returning by reference in order to be able to make changes to the original object. Your code would become
Grid& Sim::get_grid(int grid_no) {...}
Keep in mind, however, that you will not be able to return any temporaries that way (such as your dummy Grid), so you will need to change your methods to circumvent this issue. If you do not want to do this, you could still return a pointer, although this would change the syntax a little.
Your get_grid and get_swarm methods return copies of original array items. You should return reference (or a pointer) to Grid or Swarm instead.

How to access info from an instance from another instance in c++?

I am just started learning OOP concepts and to help myself learning, I have created a Characters class. From this class I have made instance called main and an instance called monster. Here is the code for the class:
#include <iostream>
#include <string>
using namespace std;
class Character {
public:
string name;
float health;
int attackLevel;
int defenseLevel;
void setAttr(string sName,float sHealth, int sAttackLevel, int sDefenseLevel) {
name = sName;
health = sHealth;
attackLevel = sAttackLevel;
defenseLevel = sDefenseLevel;
}
void attack(int whatInstanceToAttack) {
whatInstanceToAttack.hitpoints -= 20; //obviously not valid but how do i do this?
return whatInstanceToAttack;
}
int defend(string defend) {
int damageRelieved = defenseLevel * 2;
return damageRelieved;
}
};
int main() {
Character main;
Character monster;
main.setAttr("Rafael",200,100,30);
monster.setAttr("Monster1",30,40,30);
cout << "Default Values for Raf are;" << endl;
cout << main.name << endl;
cout << main.health<< endl;
cout << main.attackLevel << endl;
cout << main.defenseLevel << endl;
cout << "Default values for monster are" << endl;
cout <<monster.name << endl;
cout <<monster.health << endl;
cout << monster.attackLevel<< endl;
cout << monster.defenseLevel << endl;
return 0;
}
Basically what I want to do is somehow access the monster instance via the main instance. I want to do this by running the attack method. So if I run
main.attack(monster);
then I want the monster to lose 20 hitpoints.
How do I go about doing this?
All you need is to pass reference of Character in attack method.
I think you must be aware of pass by value and pass by reference concept. If not you can read it here
void attack(Character &whatInstanceToAttack) {
whatInstanceToAttack.hitpoints -= 20; //obviously not valid but how do i do this?
}
Yes you can access the variables of an instance from another instance of the same class. You need to use a reference to the object to ensure the changes are reflected in the other instance. So here is what your attack function should look like.
void attack(Character &c)
{
c.hitpoints - = 20;
}
Now when you call main.attack(monster) from the main() function, the hitpoints of monster will get decremented by 20.
As a side note, it is considered a good practice to make the data members of a class private, to avoid illegal access/modification of the data. Always use the member functions as an interface to your class instances.
overload the method attack and you can pass by value or reference as per your requirement.
void attack(Character chr)
or
void attack(Character &chr)

Avoid using container to call a list of functions?

I have a list of functions that return bools. I want to iterate through the list of functions and write a message for each one "Test 1 passed", "Test 2 failed" etc.
My current solution is to create a vector of function pointers, push back each function and then loop through the vector. Code below. Is there a way to avoid the container without repeating the generic message (pass/fail) code for each test (imagine there would be hundreds of tests). It feels as if the vector is unnecessary or that there must be a more elegant solution for this.
typedef bool (*Tests)();
std::vector<Tests> tests;
tests.push_back(FASTA_FILE_READER_TEST);
tests.push_back(EXACT_MATCH_TEST);
for (int i = 0; i < tests.size(); i++) {
std::cout << "Test " << i + 1
<< (tests[i]() ? " PASSED" : " FAILED")
<< std::endl;
}
Is there anything stopping you using an array?
#include <iostream>
bool FASTA_FILE_READER_TEST() { return false; }
bool EXACT_MATCH_TEST() { return false; }
int main()
{
typedef bool (*Tests)();
Tests tests[] = {FASTA_FILE_READER_TEST, EXACT_MATCH_TEST};
for (int i = 0; i < sizeof(tests)/sizeof(Tests); i++) {
std::cout << "Test " << i + 1
<< (tests[i]() ? " PASSED" : " FAILED")
<< std::endl;
}
}
You could use a function to do that:
template<typename Functor>
void test(Functor& functor){
static int i = 0;
bool ret = functor();
if(ret){
std::cout << "Test " << i++ << " passed" << std::endl;
} else {
std::cout << "Test " << i++ << " failed" << std::endl;
}
}
void main(){
test(FASTA_FILE_READER_TEST);
test(EXACT_MATCH_TEST);
}
If you can use C++11 features:
#include <array>
#include <iterator>
#include <algorithm>
#include <iostream>
typedef bool (*Test)();
std::array<Test, 2> tests {{ FASTA_FILE_READER_TEST, EXACT_MATCH_TEST }};
void TestAll()
{
size_t i = 1;
std::for_each(std::begin(tests), std::end(tests),
[&i](Test& t)
{
std::cout << "Test " << i++ << (t() ? " PASSED" : " FAILED") << std::endl;
});
}
Demo.
It's another way of doing what you've already got (and your way is just fine, IMO). If the extra capacity a vector might have set aside bothers you, you can call shrink_to_fit() on it when you're done pushing back.
Create a class for each test. Then one static instance of each class.
Contructors of classes runs tests.
This of course may cause problems, because tests are executed before main() function is called.

multimap iterator not working

I have a Playlist class that has a vector with Tracks and each Track has a multimap<long, Note> as datamember.
class Track {
private:
multimap<long, Note> noteList;
}
Using an iterator to acces the tracks is no problem, so this part here is working fine:
vector<Track>::iterator trackIT;
try{
for(noteIT = trackIT->getNoteList().begin(); noteIT != trackIT->getNoteList().end(); noteIT++){
cout << "---" << noteIT->second.getName() << endl;
}
}catch (int e){
cout << "exception #" << e << endl;
}
What I want to do next is iterate the Notes of each Track. But starting from this part all output is stopped. So I only get to see the first tracks name. Any cout's after that are not shown and the compiler isn't giving me any errors. Even the cout inside the try catch block isn't working..
vector<Track>::iterator trackIT;
multimap<long, Note>::iterator noteIT;
for(trackIT = this->playlist.getTracklist().begin(); trackIT < this->playlist.getTracklist().end(); trackIT++){
cout << trackIT->getTrackName() << endl;
for(noteIT = trackIT->getNoteList().begin(); noteIT != trackIT->getNoteList().end(); noteIT++){
cout << "---" << noteIT->second.getName() << endl;
}
}
cout << "random cout that is NOT shown" << endl; // this part doesn't show up in console either
Also, the method in my Track class that I'm using to add the Note objects looks like this:
void Track::addNote(Note &note) {
long key = 1000009;
this->noteList.insert(make_pair(key, note));
}
// I'm adding the notes to the track like this:
Note note1(440, 100, 8, 1, 1);
note1.setName("note1");
synthTrack.addNote(note1);
Any ideas why the iterator won't work?
Change
noteIT < trackIT->getNoteList().end()
To
noteIT != trackIT->getNoteList().end()
Not all iterators support less than / greater than comparisons.
If you have c++11 you can use a range-based for loop:
for (Note& note : trackIT->getNoteList())
Or you can use BOOST_FOREACH
BOOST_FOREACH (Note& note, trackIT->getNoteList())
You haven't shown the definitions of getTrackList or getNoteList, but there's a common mistake people make - if you return a copy of the container instead of a reference to it, the iterators will be pointing to different containers making comparisons impossible. Not only that but since the containers are temporary any use of the iterators results in undefined behavior.
If you are really hardcoding the track key, then there will only ever be one track in the map because std::map stores unique keys...
long key = 1000009; //If yo are really doing this, this key is already inserted so it will fail to insert more.
Also, if you would like a more elegant approach you could use function object.
struct print_track
{
void operator()(const Track& track)
{
cout << track.getTrackName() << endl;
std::for_each(track.getNoteList().begin(), track.getNoteList().end(), print_track_name());
}
};
struct print_note_name
{
void operator()(const std::pair<long,Note>& note_pair)
{
cout << "---" << note_pair.second.getName() << endl;
}
};
//In use...
std::for_each(playlist.getTracklist().begin(), playlist.getTracklist.end(), print_track());

TOUGH: Dealing with deeply nested pointers in C++

I define this structure:
struct s_molecule
{
std::string res_name;
std::vector<t_particle> my_particles;
std::vector<t_bond> my_bonds;
std::vector<t_angle> my_angles;
std::vector<t_dihedral> my_dihedrals;
s_molecule& operator=(const s_molecule &to_assign)
{
res_name = to_assign.res_name;
my_particles = to_assign.my_particles;
my_bonds = to_assign.my_bonds;
my_angles = to_assign.my_angles;
my_dihedrals = to_assign.my_dihedrals;
return *this;
}
};
and these structures:
typedef struct s_particle
{
t_coordinates position;
double charge;
double mass;
std::string name;
std::vector<t_lj_param>::iterator my_particle_kind_iter;
s_particle& operator=(const s_particle &to_assign)
{
position = to_assign.position;
charge = to_assign.charge;
mass = to_assign.mass;
name = to_assign.name;
my_particle_kind_iter = to_assign.my_particle_kind_iter;
return *this;
}
} t_particle;
struct s_bond
{
t_particle * particle_1;
t_particle * particle_2;
std::vector<t_bond_param>::iterator my_bond_kind_iter;
s_bond& operator=(const s_bond &to_assign)
{
particle_1 = to_assign.particle_1;
particle_2 = to_assign.particle_2;
my_bond_kind_iter = to_assign.my_bond_kind_iter;
return *this;
}
};
and then in my code I return a pointer to an s_molecule (typedef'd to t_molecule, but still).
Using this pointer I can get this code to work:
for (unsigned int i = 0;
i < current_molecule->my_particles.size();
i++)
{
std::cout << "Particle "
<< current_molecule->my_particles[i].name << std::endl
<< "Charge: "
<< current_molecule->my_particles[i].charge << std::endl
<< "Mass: "
<< current_molecule->my_particles[i].mass << std::endl
<< "Particle Kind Name: "
<< (*current_molecule->my_particles[i].my_particle_kind_iter).atom_kind_name
<< std::endl
<< "x: " << current_molecule->my_particles[i].position.x
<< " y: " << current_molecule->my_particles[i].position.y
#ifdef USE_3D_GEOM
<< "z: " << current_molecule->my_particles[i].position.z
#endif
<< std::endl;
}
If I replace it with:
for (std::vector<t_particle>::iterator it = current_molecule->my_particles.begin();
it !=current_molecule->my_particles.end();
it++)
{
std::cout << "Particle "
<< (*it).name << std::endl
<< "Charge: "
<< (*it).charge << std::endl
<< "Mass: "
<< (*it).mass << std::endl
<< "Particle Kind Name: "
<< (*(*it).my_particle_kind_iter).atom_kind_name
<< std::endl
<< "x: " << (*it).position.x
<< " y: " << (*it).position.y
#ifdef USE_3D_GEOM
<< "z: " << (*it).position.z
#endif
<< std::endl;
}
I now get nasty segfaults...
Not to put too much here, but I'm also getting segfaults when I tried to do this:
std::cout << "Bond ATOMS : "
<< (*current_molecule).my_bonds[0].particle_1->name
<< std::endl
Again, current_molecule is a pointer to a s_molecule structure, which contains arrays of structures, which in turn either directly have vars or are pointers. I can't get these multiple layers of indirection to work. Suggestions on fixing these segfaults.
FYI I'm compiling on Linux Centos 5.4 with g++ and using a custom makefile system.
#sbi Thanks for the good advice! I believe you are right -- the assignment overloaded operator is unnecessary and should be scrapped.
I've followed the approach of commenting out stuff and am very confused. Basically in the function that passes the pointer to my particular molecule to the main function to print, I can see all the data in that molecule (bonds, particles, name, etc) perfectly, printing with cout's.
Once I pass it to the main as a ptr, if I use that ptr with an iterator I get a segfault. In other words. Also for some reason the bond data (which I can freely print in my funct that returns to the pointer) also segfaults if I try to print it, even if I use the [] to index the vector of bonds (which works for the particle vector).
That's the best info I can give for now.
A wild guess: Are you using shared libraries. I remember having difficulties passing STL-containers back and forth across shared library boundaries.
Jason (OP) was asked in a comment by David Rodríguez:
Are you returning a pointer to a local variable?
Jason answered:
No its a ptr to a class variable. The class is very much in existence (it contains the function that returns the molecule).
Unless you're talking of a true class variable (qualified as static), the fact that the class exists doesn't have much to do with it. Instances of a class exist, and they might have ceased to exist even if you just called a function on them.
As such, the question is:
Does the instance of the class that returned the pointer current_molecule still exist?
Or is current_molecule qualified as static, i.e. being a true class variable?
If the answer to both questions is "no", you're in Undefined County.
At this point, it becomes very important that you post source code that can be used by us here to actually reproduce the problem; it might well be located in source you aren't showing us.
Again, this issue was answered here:
Weird Pointer issue in C++
by DeadMG. Sorry for the double post.