C++ What syntax for avoid duplicated declarations? - c++

I'm learning C++ and especially OO Programming.
My program use pointers to deal with Dynamic memory allocation.
While creating my default constructor, I was boring about repeat myself with
myInt = new int;
myOtherInt = new int;
etc..
So my question is : is there a way to write something like :
myInt, myOtherInt = new int;
Here's my constructor code :
Annonce::Annonce(string Titre, long double Prix, string Intro, string Description, vector<vector<string>> ImgUrls) {
titre = new string;
intro = new string;
description = new string;
imgUrls = new vector<vector<string>>;
prix = new long double;
id = new size_t;
*id = nbAnnonces;
*titre = std::move(Titre);
*prix = Prix;
*intro = std::move(Intro);
*description = std::move(Description);
*imgUrls = std::move(ImgUrls);
}

There is no shortcut for allocating multiple variables from dynamic memory.
The functions for allocating memory return a single memory address (pointer). Each variable should have its own, unique, location in memory.
The syntax of the C++ language would need to change in order to support the dynamic memory functions returning multiple addresses (pointers).
One recommendation is to reduce the quantity of memory allocations. Ask yourself, "Do I really need to allocate from dynamic memory?" before allocating from dynamic memory.

Is there a way to write something like :
myInt, myOtherInt = new int;
Yes, two ways. But the first is more general than what you specifically asked for...
1. Prefer value-types over reference-types
If you don't know what these are: Value types and Reference Types (Wikipedia).
In some programming languages, almost all of your variables, function parameters, class fields, etc - are references. A prominent example of this is Java. But in C++ - especially these days - we prefer values to references unless there's a good reason not to use values directly. Value semantics are commonly used and well-supported in the language, and most work with them is easier. See also:
Why are pointers not recommended when coding with C++11
Specifically, that means that, when defining a struct or a class, we'll give it non-pointer members. In your case:
class Announcement {
public:
using url_type = std::string; // perhaps use a URL library?
using image_urls_container_type = std::unoredered_set<url_type>;
// I'm guessing the URLs aren't really ordered
std::string title;
std::string introduction;
std::string description;
std::vector<image_urls_container_type> image_urls;
// Are the images really a sequence of indices? Shouldn't this be something like
// std::unordered_map<image_id_type,image_urls_container_type> image_urls ?
long double prize;
std::size_t id;
Announcement() = delete;
// constructors here, if you like
}
Now, I'm not saying this is necessarily the best possible definition or that it fits all your needs. But if you define it this way, you get:
No need to use new, ever.
No need to delete anything.
No chance of memory leaks (due to this class)
2. Otherwise, use std::unique_ptr's
The std::unique_ptr<T> class lets you avoid having to manage memory yourself - it takes care of that. So if you make your members std::unique_ptr's, you can initialize the value they point to using get(), e.g.:
void foo(int x) {
auto p = std::make_unique<p>();
p.get() = x;
// do stuff with p
// ...
// no need to delete p at the end of the function
}

First Pre Answer Suggestion: don't do it.
Second Pre Answer Suggestion: avoid self-managed memory allocation/deallocation and use STL containers and/or smart pointers (std::unique_ptr, std::shared_ptr, etc.) instead
Answer: no, as far I know you can't write something as
int * myInt, myOtherInt = new int;
that allocate both variables (with different allocated pointers).
But you can wrap the pointers in a class or struct that automatically allocate the contained pointer (and, maybe, destroy it in the destructor).
Just for fun... if you write a wrapper as follows
template <typename T>
struct wrappPnt
{
T * pnt = new T{};
T & operator * ()
{ return *pnt; }
T const & operator * () const
{ return *pnt; }
~wrappPnt ()
{ delete pnt; }
};
you can write your Annonce this way
struct Annonce
{
wrappPnt<std::string> titre, intro, description;
wrappPnt<std::vector<std::vector<std::string>>> imgUrls;
wrappPnt<long double> prix;
Annonce (std::string Titre, long double Prix, std::string Intro,
std::string Description,
std::vector<std::vector<std::string>> ImgUrls)
{
*titre = std::move(Titre);
*prix = Prix;
*intro = std::move(Intro);
*description = std::move(Description);
*imgUrls = std::move(ImgUrls);
}
};
First Post Answer Suggestion: don't do it.
Second Post Answer Suggestion: avoid self-managed memory allocation/deallocation and use STL containers and/or smart pointers (std::unique_ptr, std::shared_ptr, etc.) instead

Related

A question about dynamic memory allocation 2

After a few years, I discovered a memory leak bug in my code. Unfortunately the bug was not causing any issues to be noticed until I found out about it indirectly.
Below is a function addElement() from class c_string which adds a new element to a chain. My old way of doing it is like this:
class c_string
{
private:
char *chain;
size_t chain_total;
public:
void addElement(char ch);
};
void c_string::addElement(char ch)
{
char *tmpChain = new char[chain_total+1];
for(size_t i=0;i<chain_total;i++)
tmpChain[i] = chain[i];
tmpChain[chain_total++] = ch;
delete chain;
chain = new char[chain_total];
chain = tmpChain;
}
I found out that by doing chain = tmpChain; I am causing a memory leak.
My question is, what is the best way to handle it?
Please note, in this class I am not using any STL function, because I am simply writing my own string.
The best way to do it is simply drop the second allocation, it serves no purpose.
void c_string::addElement(char ch)
{
char *tmpChain = new char[chain_total+1];
for(size_t i=0;i<chain_total;i++)
tmpChain[i] = chain[i];
tmpChain[chain_total++] = ch;
delete[] chain;
chain = tmpChain;
}
The above is correct and even has the strong exception guarantee.
Of course even if you do not want to use std::string, std::unique_ptr would is still safer than raw new+delete and you would get the rule of five for free instead of having to implement it on your own.
From performance standpoint, you might be interested in Why is it common practice to double array capacity when full? and of course std::memcpy or std::copy instead of the manual loop.

C++ , how come I do not get the value "123456"

I am trying to print out value 123456, but it gives me the garbage value. How can I fix it? And Can you please explain why it gives the wrong value?
#include <stdio.h>
#include <stdlib.h>
struct MyInfo
{
private:
int private_key = 123456;
public:
int setkey(int value)
{
private_key = value;
}
int GetScore()
{
return private_key;
}
};
void main()
{
MyInfo* pMyInfo;
pMyInfo = (MyInfo*)malloc(sizeof(MyInfo));
printf("%d\n", pMyInfo->GetScore());
free(pMyInfo);
}
Don't use malloc/free but rather pMyInfo = new MyInfo() and delete pMyInfo. Only new will call the constructor which initializes the value; only delete will call the destructor.
Regarding the comment, what is meant is, you can also have it on the stack, i.e. MyInfo pMyInfo;, i.e. not a pointer. That will automatically call the constructor and when it goes out of scope, the destructor.
int private_key = 123456;
This really is just a camouflaged constructor initialization which means it's the same as:
MyInfo() : private_key(123456) {}
Since malloc and friends are inherited from C and C has no classes (and thus no special member functions) whatsoever malloc and friends won't call these necessary special member functions to set up your object. The C++ equivalent new does however which is why you should always use new over malloc and delete over free.
But wait, there's more...
Actually, you shouldn't ever use new either, there are always better alternatives than using raw dynamic allocation. If you really need dynamic memory allocation then use std::unique_ptr or for multiple objects std::vector but most of the time you don't even need these ( there are tons of posts on here that explain when dynamic allocation is a must, for all the other cases just use storage with automatic lifetime) all you need in this case is a local object:
MyInfo myInfo;
printf("%d\n", myInfo.GetScore());
See how your code just got shorter, easier to maintain and cleaner to achieve the same?
When you declare a pointer of type MyInfo, it does not mean that the object it points to will actually be your struct, it just assumes it will be.
When you do malloc(sizeof(MyInfo)), you simply allocate memory of the size which your struct might take, it does not create an object. Hence, when you try to do GetScore(), it accesses memory location which it assumes contains your private_key, but instead it simply contains garbage.
Don't mix C and C++
You should avoid malloc/alloc etc in C++ and opt for new operator if you want to work with dynamically allocated objects.
Add a constructor to initialize the value
private;
int private_key;
public:
MyInfo () {
private_key = 123456;
}
And implement the main like
// without pointer
void main () {
MyInfo myinfo;
printf("%d\n", myinfo.GetScore());
}
// with pointer
void main () {
MyInfo *myinfo = new MyInfo();
printf("%d\n", myinfo->GetScore());
}
Just for reference, it is possible to initialize an object in raw storage, but it would be overkill and rather stupid for this use case. As malloc only allocate raw memory and does not construct an object, you could use a placement new to build the object in a second time:
int main() // I can't stand void main
{
MyInfo* pMyInfo;
pMyInfo = (MyInfo*)malloc(sizeof(MyInfo)); // only allocate raw memory
new((void *) pMyInfo) MyInfo; // construct the object
std::cout << pMyInfo->GetScore() << std::endl; // no reason for C printf here
pMyInfo->~MyInfo(); // placement new requires explicit destructor call if not trivial
free(pMyInfo);
return 0;
}
DO NOT DO THAT for such a simple case. Placement new should only be used in very special cases where the allocation is not trivial, for example when you use share memory. But here the correct way is to simply use an automatic object:
int main() // I can't stand void main
{
MyInfo pMyInfo;
std::cout << pMyInfo.GetScore() << std::endl;
return 0;
}

Use constructor to allocate memory

I have a class that contains several arrays whose sizes can be determined by parameters to its constructor. My problem is that instances of this class have sizes that can't be determined at compile time, and I don't know how to tell a new method at run time how big I need my object to be. Each object will be of a fixed size, but different instances may be different sizes.
There are several ways around the problem:- use a factory- use a placement constructor- allocate arrays in the constructor and store pointers to them in my object.
I am adapting some legacy code from an old application written in C. In the original code, the program figures out how much memory will be needed for the entire object, calls malloc() for that amount, and proceeds to initialize the various fields.
For the C++ version, I'd like to be able to make a (fairly) normal constructor for my object. It will be a descendant of a parent class, and some of the code will be depending on polymorphism to call the right method. Other classes descended from the same parent have sizes known at compile time, and thus present no problem.
I'd like to avoid some of the special considerations necessary when using placement new, and I'd like to be able to delete the objects in a normal way.
I'd like to avoid carrying pointers within the body of my object, partially to avoid ownership problems associated with copying the object, and partially because I would like to re-use as much of the existing C code as possible. If ownership were the only issue, I could probably just use shared pointers and not worry.
Here's a very trimmed-down version of the C code that creates the objects:
typedef struct
{
int controls;
int coords;
} myobject;
myobject* create_obj(int controls, int coords)
{
size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);
char* mem = malloc(size);
myobject* p = (myobject *) mem;
p->controls = controls;
p->coords = coords;
return p;
}
The arrays within the object maintain a fixed size of the life of the object. In the code above, memory following the structure of myobject will be used to hold the array elements.
I feel like I may be missing something obvious. Is there some way that I don't know about to write a (fairly) normal constructor in C++ but be able to tell it how much memory the object will require at run time, without resorting to a "placement new" scenario?
How about a pragmatic approach: keep the structure as is (if compatibility with C is important) and wrap it into a c++ class?
typedef struct
{
int controls;
int coords;
} myobject;
myobject* create_obj(int controls, int coords);
void dispose_obj(myobject* obj);
class MyObject
{
public:
MyObject(int controls, int coords) {_data = create_obj(controls, coords);}
~MyObject() {dispose_obj(_data);}
const myobject* data() const
{
return _data;
}
myobject* data()
{
return _data;
}
int controls() const {return _data->controls;}
int coords() const {return _data->coords;}
double* array() { return (double*)(_data+1); }
private:
myobject* _data;
}
While I understand the desire to limit the changes to the existing C code, it would be better to do it correctly now rather than fight with bugs in the future. I suggest the following structure and changes to your code to deal with it (which I suspect would mostly be pulling out code that calculates offsets).
struct spots
{
double x;
double y;
};
struct myobject
{
std::vector<double> m_controls;
std::vector<spots> m_coordinates;
myobject( int controls, int coordinates ) :
m_controls( controls ),
m_coordinates( coordinates )
{ }
};
To maintain the semantics of the original code, where the struct and array are in a single contigious block of memory, you can simply replace malloc(size) with new char[size] instead:
myobject* create_obj(int controls, int coords)
{
size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);
char* mem = new char[size];
myobject* p = new(mem) myobject;
p->controls = controls;
p->coords = coords;
return p;
}
You will have to use a type-cast when freeing the memory with delete[], though:
myobject *p = create_obj(...);
...
p->~myobject();
delete[] (char*) p;
In this case, I would suggest wrapping that logic in another function:
void free_obj(myobject *p)
{
p->~myobject();
delete[] (char*) p;
}
myobject *p = create_obj(...);
...
free_obj(p);
That being said, if you are allowed to, it would be better to re-write the code to follow C++ semantics instead, eg:
struct myobject
{
int controls;
int coords;
std::vector<double> values;
myobject(int acontrols, int acoords) :
controls(acontrols),
coords(acoords),
values(acontrols + acoords*2)
{
}
};
And then you can do this:
std::unique_ptr<myobject> p = std::make_unique<myobject>(...); // C++14
...
std::unique_ptr<myobject> p(new myobject(...)); // C++11
...
std::auto_ptr<myobject> p(new myobject(...)); // pre C++11
...
New Answer (given comment from OP):
Allocate a std::vector<byte> of the correct size. The array allocated to back the vector will be contiguous memory. This vector size can be calculated and the vector will manage your memory correctly. You will still need to be very careful about how you manage your access to that byte array obviously, but you can use iterators and the like at least (if you want).
By the way here is a little template thing I use to move along byte blobs with a little more grace (note this has aliasing issues as pointed out by Sergey in the comments below, I'm leaving it here because it seems to be a good example of what not to do... :-) ) :
template<typename T>
T readFromBuf(byte*& ptr) {
T * const p = reinterpret_cast<T*>(ptr);
ptr += sizeof(T);
return *p;
}
Old Answer:
As the comments suggest, you can easily use a std::vector to do what you want. Also I would like to make another suggestion.
size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);
The above line of code suggests to me that you have some "hidden structure" in your code. Your myobject struct has two int values from which you are calculating the size of what you actually need. What you actually need is this:
struct ControlCoord {
double control;
std::pair<double, double> coordinate;
};
std::vector<ControlCoord>> controlCoords;
When the comments finally scheded some light on the actual requirements, the solution would be following:
allocate a buffer large enough to hold your object and the array
use placement new in the beginning of the buffer
Here is how:
class myobject {
myobject(int controls, int coords) : controls(controls), coords(coords) {}
~myobject() {};
public:
const int controls;
const int coords;
static myobject* create(int controls, int coords) {
std::unique_ptr<char> buffer = new char[sizeof(myobject) + (controls + coords*2) * sizeof(double)];
myobject obj* = new (buffer.get()) myobject(controls, coords);
buffer.release();
return obj;
}
void dispose() {
~myobject();
char* p = (char*)this;
delete[] p;
}
};
myobject *p = myobject::create(...);
...
p->dispose();
(or suitably wrapped inside deleter for smart pointer)

Vector of Object Pointers, general help and confusion

Have a homework assignment in which I'm supposed to create a vector of pointers to objects
Later on down the load, I'll be using inheritance/polymorphism to extend the class to include fees for two-day delivery, next day air, etc. However, that is not my concern right now. The final goal of the current program is to just print out every object's content in the vector (name & address) and find it's shipping cost (weight*cost).
My Trouble is not with the logic, I'm just confused on few points related to objects/pointers/vectors in general. But first my code. I basically cut out everything that does not mater right now, int main, will have user input, but right now I hard-coded two examples.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Package {
public:
Package(); //default constructor
Package(string d_name, string d_add, string d_zip, string d_city, string d_state, double c, double w);
double calculateCost(double, double);
~Package();
private:
string dest_name;
string dest_address;
string dest_zip;
string dest_city;
string dest_state;
double weight;
double cost;
};
Package::Package()
{
cout<<"Constucting Package Object with default values: "<<endl;
string dest_name="";
string dest_address="";
string dest_zip="";
string dest_city="";
string dest_state="";
double weight=0;
double cost=0;
}
Package::Package(string d_name, string d_add, string d_zip, string d_city, string d_state, string r_name, string r_add, string r_zip, string r_city, string r_state, double w, double c){
cout<<"Constucting Package Object with user defined values: "<<endl;
string dest_name=d_name;
string dest_address=d_add;
string dest_zip=d_zip;
string dest_city=d_city;
string dest_state=d_state;
double weight=w;
double cost=c;
}
Package::~Package()
{
cout<<"Deconstructing Package Object!"<<endl;
delete Package;
}
double Package::calculateCost(double x, double y){
return x+y;
}
int main(){
double cost=0;
vector<Package*> shipment;
cout<<"Enter Shipping Cost: "<<endl;
cin>>cost;
shipment.push_back(new Package("tom r","123 thunder road", "90210", "Red Bank", "NJ", cost, 10.5));
shipment.push_back(new Package ("Harry Potter","10 Madison Avenue", "55555", "New York", "NY", cost, 32.3));
return 0;
}
So my questions are:
I'm told I have to use a vector
of Object Pointers, not Objects.
Why? My assignment calls for it
specifically, but I'm also told it
won't work otherwise.
Where should I be creating this
vector?
Should it be part of my Package
Class? How do I go about adding
objects into it then?
Do I need a copy constructor? Why?
What's the proper way to deconstruct
my vector of object pointers?
Any help would be appreciated. I've searched for a lot of related articles on here and I realize that my program will have memory leaks. Using one of the specialized ptrs from boost:: will not be available for me to use. Right now, I'm more concerned with getting the foundation of my program built. That way I can actually get down to the functionality I need to create.
Thanks.
A vector of pointers can be reused for storing objects of sub-classes:
class Person
{
public:
virtual const std::string& to_string () = 0;
virtual ~Person () { }
};
class Student : public Person
{
const std::string& to_string ()
{
// return name + grade
}
};
class Employee : public Person
{
const std::string& to_string ()
{
// return name + salary
}
};
std::vector<Person*> persons;
person.push_back (new Student (name, grade));
person.push_back (new Employee (name, salary));
person[0]->to_string (); // name + grade
person[1]->to_string (); // name + salary
Ideally the vector should be wrapped up in a class. This makes memory management easier. It also facilitates changing the support data structure (here an std::vector) without breaking existing client code:
class PersonList
{
public:
Person* AddStudent (const std::string& name, int grade)
{
Person* p = new Student (name, grade);
persons.push_back (p);
return p;
}
Person* AddEmployee (const std::string& name, double salary)
{
Person* p = new Employee (name, salary);
persons.push_back (p);
return p;
}
~PersonList ()
{
size_t sz = persons.size ();
for (size_t i = 0; i < sz; ++i)
delete persons[i];
}
private
std::vector<Person*> persons;
};
So we can re-write our code as:
{
PersonList persons;
Person* student = persons.AddStudent (name, grade);
Person* employee = persons.AddEmployee (name, salary);
student.to_string ();
employee.to_string ();
} // The memory allocated for the Person objects will be deleted when
// `persons` go out of scope here.
Getting familiar with the Rule of Three will help you decide when to add a copy constructor to a class. Also read about const correctness.
Question 1:
You mentioned inheritance. Since inherited objects often need more bytes of storage, they don't fit into the place of a base object. If you try to put them in, you get a base object instead. This is called object slicing.
Question 2:
Design first, before you write code. There are a bunch of possible solutions.
For a start you can keep it in main(), but later you will be forced to make a class like PackageContainer for holding your objects.
Question 3 + 4:
You need a copy constructor, an assignment operator= and a destructor, when a class object owns dynamically allocated objects (the Rule of the Big Three). So a PackageContainer will probably need them.
You create objects dynamically using new Object(..). You are responsible for destroying them and for giving their memory back to the system immediately before your vector of pointers is destroyed:
for (size_t i = 0; i < shipment.size(); ++i)
{
delete shipment[i];
}
Since working with naked pointers to dynamically allocated objects is not safe, consider using
std::vector<tr1::shared_ptr<Package> > shipment;
instead or
std::vector<std::shared_ptr<Package> > shipment;
if your compiler understands C++0x. The shared_ptr handles freeing memory for you: It implements the Rule of the Big Three for one object pointer. It should be used in production quality code.
But try to get it right with naked pointers also. I think that's what your homework assignment is about.
I'm told I have to use a vector of Object Pointers, not Objects. Why? My assignment calls for it specifically, but I'm also told it won't work otherwise.
Usually, one would avoid using vector of objects to avoid the problem of Object Slicing. To make polymorphism work You have to use some kind of pointers. I am not sure of how the classes in your assignment are aligned but probably you might have Inheritance there somewhere and hence if vector is storing objects of Base class and you insert objects of Derived class in it then it would cause the derived class members to slice off.
The Best solution will be to use a smart pointer instead of a Raw pointer. The STL has an auto_ptr, but that cannot be used in a standard container.Boost smart pointers would be a best solution but as you already said you can't use Boost So in your case you can use your compiler's implementation of smart pointers, which comes in TR1 namespace,remember though that there is some disagreement on the namespace for TR1 functions (Visual C++ puts them in std::, while GCC puts them in std::tr1::).
Where should I be creating this vector? Should it be part of my Package Class? How do I go about adding objects into it then?
Your example code already has an example of adding a pointer to Package class in a vector. In a nutshell you will dynamically allocate pointers to Package and then add them to the vector.
Do I need a copy constructor? Why?
The copy constructor generated by the compiler does member-wise copying. Sometimes that is not sufficient. For example:
class MyClass {
public:
MyClass( const char* str );
~MyClass();
private:
char* str;
};
MyClass::MyClass( const char* str2 )
{
str = new char[srtlen( str2 ) + 1 ];
strcpy( str, str2 );
}
Class::~Class()
{
delete[] str;
}
In this case member-wise copying of str member will not duplicate the buffer (only the pointer will be copied(shallow copy)), so the first to be destroyed copy sharing the buffer will call delete[] successfully and the second will run into Undefined Behavior. You need deep copying copy constructor (and assignment operator as well) in such a scenario.
When to use a custom copy constructor is best defined by the Rule Of Three:
Whenever you are writing either one of Destructor, Copy Constructor or Copy Assignment Operator, you probably need to write the other two.
What's the proper way to deconstruct my vector of object pointers?
You will have to explicitly call delete on each contained pointer to delete the content it is pointing to.
vector::erase
Removes from the vector container and calls its destructor but If the contained object is a pointer it doesnt take ownership of destroying it.
Check out this answer here to know how to corrctly delete a vector of pointer to objects.

std::map inizialitazion (only one time)

I have a function that translates data using std::map
struct HistoParameter
{
int nbins;
float first;
float last;
HistoParameter(int _nbins, int _first, int _last) :
nbins(_nbins), first(_first), last(_last) {};
};
HistoParameter* variable_to_parameter(char* var_name)
{
std::map<const std::string, HistoParameter*> hp;
hp[std::string("ph_pt")] = new HistoParameter(100,0,22000);
hp[std::string("ph_eta")] = new HistoParameter(100,-3,3);
// ...
return hp[var_name];
}
My struct is very light, but image it can be heavy. The prolem is that every time I call this function it create a lot of HistoParameter objects, maybe a switch case is more efficient. First question: I'm creating garbage?
Second solution:
bool first_time = true;
HistoParameter* variable_to_parameter(char* var_name)
{
static std::map<const std::string, HistoParameter*> hp;
if (first_time)
{
hp[std::string("ph_pt")] = new HistoParameter(100,0,22000);
hp[std::string("ph_eta")] = new HistoParameter(100,-3,3);
// ...
}
first_time = false;
return hp[var_name];
is it ok? Better solution?
The second solution seems OK to me - you can say:
if ( hp.empty() ) {
// populate map
}
I would also consider making it a map of values rather than pointers - I don't see you need dynamic allocation here:
std::map <std::string, HistoParameter> hp;
then:
hp["ph_pt"] = HistoParameter(100,0,22000);
Note you don't need the explicit std::string conversion. Or better still:
hp.insert( std::make_pair( "ph_pt", HistoParameter(100,0,22000 )));
The first solution produces a lot of garbage. Why don't you return the class by value? It's quite lightweight, and you wouln't have to dynamically allocate it.
HistoParameter variable_to_parameter(char* var_name)
{
static std::map<const std::string, HistoParameter> hp;
if ( hp.empty() )
{
hp.insert( std::make_pair( "ph_pt", HistoParameter(100,0,22000) ) );
hp.insert( std::make_pair( "ph_eta", HistoParameter(100,-3,3) ) );
//...
}
return hp[var_name];
}
If the class returned gets larger, and you want a power-tool, then try out boost::flyweight.
If you don't want to pass back a big structure, you can do:
HistoParameter& variable_to_parameter(char* var_name)
{
// same code
}
... and even throw in a const if you want it immutable.
Edit: added make_pair, as suggested by Niel.
Your second solution should certainly improve efficiency, but isn't (at least IMO) the best implementation possible. First of all, it makes first_time publicly visible, even though only variable_to_parameter actually cares about it. You've already made hp a static variable in the function, and first_time should be as well.
Second, I would not use pointers and/or dynamic allocation for the HistoParameter values. At one int and two floats, there's simply no reason to do so. If you're really passing them around so much that copying became a problem, you'd probably be better off using some sort of smart pointer class instead of a raw pointer -- the latter is more difficult to use and much more difficult to make exception safe.
Third, I'd consider whether it's worthwhile to make variable_to_parameter into a functor instead of a function. In this case, you'd initialize the map in the ctor, so you wouldn't have to check whether it was initialized every time operator() was invoked. You can also combine the two, by have a static map in the functor. The ctor initializes it if it doesn't exist, and operator() just does a lookup.
Finally, I'd note that map::operator[] is primarily useful for inserting items -- it creates an item with the specified key if it doesn't exist, but when you're looking for an item, you usually don't want to create an item. For this, you're generally better off using map.find() instead.
I'd have a std::map< std::string, HistoParameter *> member and do
InitializeHistoParameter()
{
myMap["ph_pt"] = new ...
myMap["ph_eta"] = new ...
}
And then
HistoParameter* variable_to_parameter(char* var_name)
{
return myMap[var_name];
}
either way, you are creating memory leak.
each time the = operator is called, for example:
hp[std::string("ph_pt")] = new HistoParameter(100,0,22000);
you are creating a new HistoParameter object and pairing the key "ph" with this most recent object, leaving the previous one dangling.
If creating a new object each time is your actual intent, you probably need to call
delete hp[std::string("ph_pt")];
before the new operation.
My suggestion is to avoid raw new operations as much as possible and resort to smart pointers such as boost::share_ptr for object life time management.