Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is a good place to put a const in the following C++ statement

Tags:

c++

Consider the following class member:

std::vector<sim_mob::Lane *>  IncomingLanes_;

the above container shall store the pointer to some if my Lane objects. I don't want the subroutins using this variable as argument, to be able to modify Lane objects. At the same time, I don't know where to put 'const' keyword that does not stop me from populating the container.

could you please help me with this?

thank you and regards vahid

Edit: Based on the answers i got so far(Many Thanks to them all) Suppose this sample:

#include <vector>
#include<iostream>
using namespace std;

class Lane
{
private:
    int a;
public:
    Lane(int h):a(h){}
    void setA(int a_)
    {
        a=a_;
    }
    void printLane()
    {
        std::cout << a << std::endl;
    }
};

class B
{

public:
    vector< Lane const  *> IncomingLanes;
    void addLane(Lane  *l)
    {
        IncomingLanes.push_back(l);
    }

};

int main()
{
    Lane l1(1);
    Lane l2(2);
    B b;
    b.addLane(&l1);
    b.addLane(&l2);
    b.IncomingLanes.at(1)->printLane();
    b.IncomingLanes.at(1)->setA(12);
    return 1;
}

What I meant was:

b.IncomingLanes.at(1)->printLane()

should work on IncomingLanes with no problem AND

b.IncomingLanes.at(1)->setA(12)

should not be allowed.(In th above example none of the two mentioned methods work!)

Beside solving the problem, I am loking for good programming practice also. So if you think there is a solution to the above problem but in a bad way, plase let us all know. Thaks agian

like image 209
rahman Avatar asked May 03 '12 07:05

rahman


3 Answers

A detour first: Use a smart pointer such shared_ptr and not raw pointers within your container. This would make your life a lot easy down the line.

Typically, what you are looking for is called design-const i.e. functions which do not modify their arguments. This, you achieve, by passing arguments via const-reference. Also, if it is a member function make the function const (i.e. this becomes const within the scope of this function and thus you cannot use this to write to the members).

Without knowing more about your class it would be difficult to advise you to use a container of const-references to lanes. That would make inserting lane objects difficult -- a one-time affair, possible only via initializer lists in the ctor(s).

A few must reads:

  • The whole of FAQ 18
  • Sutter on const-correctness

Edit: code sample:

#include <vector>
#include <iostream>
//using namespace std; I'd rather type the 5 characters

// This is almost redundant under the current circumstance
#include <vector>
#include <iostream>
#include <memory>
//using namespace std; I'd rather type the 5 characters

// This is almost redundant under the current circumstance
class Lane
{
private:
    int a;
public:
    Lane(int h):a(h){}
    void setA(int a_) // do you need this?
    {
        a=a_;
    }
    void printLane() const // design-const
    {
        std::cout << a << std::endl;
    }
};

class B
{    
    // be consistent with namespace qualification
    std::vector< Lane const * > IncomingLanes; // don't expose impl. details
 public:
    void addLane(Lane const& l) // who's responsible for freeing `l'?
    {
        IncomingLanes.push_back(&l); // would change
    }
    void printLane(size_t index) const
    {
#ifdef _DEBUG 
        IncomingLanes.at( index )->printLane();
#else
        IncomingLanes[ index ]->printLane();
#endif
    }        
};

int main()
{
    Lane l1(1);
    Lane l2(2);
    B b;
    b.addLane(l1);
    b.addLane(l2);
    //b.IncomingLanes.at(1)->printLane(); // this is bad
    //b.IncomingLanes.at(1)->setA(12); // this is bad
    b.printLane(1);

    return 1;
}

Also, as Matthieu M. suggested:

shared ownership is more complicated because it becomes difficult to tell who really owns the object and when it will be released (and that's on top of the performance overhead). So unique_ptr should be the default choice, and shared_ptr a last resort.

Note that unique_ptrs may require you to move them using std::move. I am updating the example to use pointer to const Lane (a simpler interface to get started with).

like image 190
dirkgently Avatar answered Nov 15 '22 05:11

dirkgently


You can do it this way:

std::vector<const sim_mob::Lane *>  IncomingLanes_;

Or this way:

std::vector<sim_mob::Lane const *>  IncomingLanes_;

In C/C++, const typename * and typename const * are identical in meaning.

Updated to address updated question:

If really all you need to do is

b.IncomingLanes.at(1)->printLane()

then you just have to declare printLane like this:

void printLane() const // Tell compiler that printLane doesn't change this
  {
  std::cout << a << std::endl;
  }
like image 21
TonyK Avatar answered Nov 15 '22 03:11

TonyK


I suspect that you want the object to be able to modify the elements (i.e., you don't want the elements to truly be const). Instead, you want nonmember functions to only get read-only access to the std::vector (i.e., you want to prohibit changes from outside the object).

As such, I wouldn't put const anywhere on IncomingLanes_. Instead, I would expose IncomingLanes_ as a pair of std::vector<sim_mob::Lane *>::const_iterators (through methods called something like GetIncomingLanesBegin() and GetIncomingLanesEnd()).

like image 35
Max Lybbert Avatar answered Nov 15 '22 04:11

Max Lybbert