Solved

const and member list initialisation

Posted on 2003-12-04
19
762 Views
Last Modified: 2010-04-01
Suppose I have a very simple class

class One
{
public:
     One();
private:
     const int a;
     char* m;<---------------(1)
};

One::One()
{
     char z[] = "String";
     m = new char[strlen(z) + 1];
     strcpy(m, z);
     //a = 10; <---------------(2)
}

Now, I have stated that 'a' is const.  The compiler moans if I leave (2) as-is, stating that 'a' must be initialised in the initialisation list.  So, first question is why does a const variable *have* to be initialised in the initialisation list, not just in the constructor body ?

Secondly, if I change (1) to be a const char*, and try the strcpy as in the body of the constructor, the compiler moans about not being able to convert parameter 1 from const char* to char*.  Now, this problem vanishes when I initialise 'm' in the initialisation list (eg m("String") //unsafe I know ), again reinforcing the fact that const variables must be initialised in this manner.  But what I would liike to learn now is what if I wanted to leave 'm' as const; I obviously cannot safely assign to m a string literal (as above); the memory has to be allocated first hence my call to 'new'.  How would I go about this, or is the simple answer "I couldn't".

?

Cheers in advance.
0
Comment
Question by:mrwad99
  • 6
  • 5
  • 4
  • +2
19 Comments
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>So, first question is why does a const variable *have* to be initialised in the initialisation list

A constant value can not be modify.
The body of a constructor has to go by the same rules as any other member function, in that it can not modify constant objects.
The initialize list allows the class to set the value before the object is used, and before it enters the body of the constructor.

0
 
LVL 30

Accepted Solution

by:
Axter earned 20 total points
Comment Utility
Example code:

class foo
{
public:
      foo(const char* Data):m(MyInitFunc(Data))
      {
      }
      ~foo(){delete (char*)m;} //Casting only needed for non-compliant compilers like VC++ 6.0
      const char* m;
private:
      const char* MyInitFunc(const char* Data)
      {
            char* p = new char[strlen(Data)+1];
            strcpy(p, Data);
            return p;
      }
};


int main(int argc, char* argv[])
{
      char somedata[] = "Hello Axter";
      foo myfoo(somedata);
      cout << myfoo.m << endl;
      
      system("pause");
      return 0;
}

0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
You can safely get around the initialize problem for a const char* type by creating a initialize function.  The initiailize function can be a member function or a global function.

In the above example, I passed in a string to the constructor, which is then used in the initialize function to determine the length of the buffer to create, and the value to set the constant data.

According to the C++ standard, you should be able to delete a const pointer without having to cast away the constant.
However, some compilers are not fully compliant.  VC++ 6.0 has this problem.
The proper way to bypass this problem is to use const_cast.
Example:
~foo(){delete const_cast<char*>(m);}
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
Here's another method that uses less code, but requires more memory space per object.

class foo
{
      char *m_Real;
public:
      foo(const char* Data):m(m_Real)
      {
            m_Real = new char[strlen(Data)+1];
            strcpy(m_Real, Data);
      }
      ~foo(){delete m_Real;}
public:
      const char* &m;
};

The above class creates a constant pointer reference which is initialized to a non-constant pointer.
Your constuctor can then modify what "m" is pointing to, but external access is still constant since "m" is constant and m_real is private.

This method gives the object the ability to modify that object as needed, but outside exposure is still constant.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
if you have a constructor that has a const char* as argument you don't need an initializer function to initialize the const member:

class A
{
private:
     const char* m_psz;

public:
     A(const char* psz) : m_psz(psz) {}
};

You shouldn't delete that pointer in the destructor because it is not very likely that it is created by 'new';

void main()
{
     A("anyConstantString");
}

Regards, Alex

0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>if you have a constructor that has a const char* as argument you don't need an initializer function to initialize the const member:
>>A("anyConstantString");

This method is not safe.  It will work in most compilers, but it's undefined according to the C++ standard.

If the goal is to get a constant copy of the string that is passed in, then you do need to use one of the above methods I posted.

Also, if you're not passing in a string at all, as in the questioner's example code, then the above method is not pratical.
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Axter,

>> foo(const char* Data):m(m_Real)


so essentially what you are doing here is to point a pointer at another pointer temporarily ?  

Is this safe ?  Does it not mean that m will be pointing to nothing ?

Cheers again.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
A problem arises when the class instance lives longer than const char*, because the member pointer might become invalid.

A* f()
{
     A* pA = new A("Any Const String");
     return A;
}

void main ()
{
    A* pa = f();
   
    // Here the const char* member of pA might be invalid
   
    char buffer[100];
    strcpy( buffer, pA->getString() );   // This statement might crash

}

Regards, Alex
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Alex,

I am aware of what you have said.  In the example you first gave,

 A(const char* psz) : m_psz(psz) {}

caused a crash because no memory is being allocated.  This is exactly the same in your above example.  What I want to know is in the example of Axter, is pointing a pointer at another pointer safe or not, if the pointer being pointed to is not initialised ?
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 2

Assisted Solution

by:sin_
sin_ earned 20 total points
Comment Utility
without going into what's going on, I shall try to answer your questions.

1. So, first question is why does a const variable *have* to be initialised in the initialisation list, not just in the constructor body ?

>> As you know, for a const variable, initialization has to happen only when you declare it. The initialization list for a class ctor is different from an assignment.

say example:
class B;
class A {
A(const B& b1);
private:
B b
const int a;
};

so when you say:

A::A() {
b = b1;
}

You are actually calling the assignment operator of class B, which could be a really costly operation as compared to a simple initialization...so now you know that the initialiazation and assignment are different.

Now as you know the above, you should be able to point out that if you don't put your const data inside the initialization list, you will get an error because you are trying to assign something to your const data member which is wrong :-)

Now your second question:

Secondly, if I change (1) to be a const char*, and try the strcpy as in the body of the constructor, the compiler moans about not being able to convert parameter 1 from const char* to char*.  

>> in a scenario like this:
void foo(char* p);

//calling it,
foo("hello"); simply gives you a compilation error, becaue the string "hello" is a constant string. So if you change the char* p to const char* p, it will work.

guess you got your answer..never mind if you haven't gotten it yet...

you can pass a non-const param to a function which expects a const data. But you can't ever pass a const param to a function which expects non-const data.

Hope it helps!

-sin
0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 20 total points
Comment Utility
the foo example works and is safe because both pointers m and m_Real are valid as long as the class instance exists and
both have the same pointer value.

There is a short moment in


     foo(const char* Data):m(m_Real)
     {
          m_Real = new char[strlen(Data)+1];
          strcpy(m_Real, Data);
     }

where both pointers have a pointer value that is not initialized. But with

    m_Real = new char[strlen(Data)+1];

pointers get valid. And this is safe, because no one could access the foo object before construction is done.
With

     foo(const char* Data):m_Real(NULL), m(m_Real)
     {
          m_Real = new char[strlen(Data)+1];
          strcpy(m_Real, Data);
     }

both members get initialized.
     
It is good practice to initialize all data members that have no default constructor in the initializer list.

I would recommend to use the initializer function and not two pointers because of two reasons:

1. both solutions will safely initialize a const member and the function is less tricky.
2. the purpose of a const member is to be const. if i need a member that must be changed why make it const?

Regards, Alex
   
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Right, it seems that I have got three answers here all from different people that have all taught me something new !  Consequently I am going to split the points, but before I do...

sin_,

>>//calling it,
foo("hello"); simply gives you a compilation error, becaue the string "hello" is a constant string. So if you change the char* p to const char* p, it will work.

You say this is a constant string, fine.  But where exactly in memory is it ?  And how does the compiler know to alllocate 6 bytes (in this instance) ?  Is this not dangerous also ?  Also, in C programs especially (not so much C++; it has the string class), should I always (when declaring a char*) use new or malloc to allocate memory first ?  Or does it not matter ?

EG simple instances like

char* m= "Hello world";  // OK, or should I use malloc/new ??
printf(m);

Cheers again.
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>char* m= "Hello world";  // OK, or should I use malloc/new ??

You should not have a non-constant pointer pointing to a string literal.

This code is depricated according to the C++ standard.

You should use a constant poitner instead.
const char* m= "Hello world";

Some compilers will allow you to modify a string literal, and some compilers will crash.
Example, VC++ 5.0 will allow you to modify a string literal.  VC++6.0 will crash if you try to modify a string literal.
Modifying a string literal is considered undefined behavior according to the C++ standard.
0
 
LVL 2

Expert Comment

by:sin_
Comment Utility
hello,
Axter has given some tips to ya. So I won't cover that again. But I've something to add up.

You have found it yourself that you're allocating the memory on heap, so it may crahs later when you say char* p = "hello";
Because the ptr is pointing to a memory location on the stack. So it's not safe. ..
but when you say, cont char* p = "hello";, it's safe because the memory allocation happens at a place where all the const and global variables are kept.

Hope that helps
-sin
0
 
LVL 30

Expert Comment

by:mayankeagle
Comment Utility
Hi Axter and others, Sorry this is out of this question, but could you throw some light on:

http://www.experts-exchange.com/Programming/Q_20816080.html

I'm sure your rich knowledge has a lot which you can share on that page....
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
<Increased points to 60 for a three way split>

>>cont char* p = "hello";, it's safe because the memory allocation happens at a place where all the const and global variables are kept.

So essentially this memory will not go out of scope until the program exits ?  

I still think it is bad form to pass a char* to a constructor and directly assign it to the char* member variable via the initialisation list as above; even if the char* being passed in is const hence will not go out of scope.  Correct ?
0
 
LVL 2

Expert Comment

by:sin_
Comment Utility
yeah you are right when you say that when you make your const char* point to a variable, the memory won't go out of scope.

for a raw char*, initialization isn't needed and you are right that it shouldn't be done.

-sin
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
Yes, any object should have it's own memory, so you should'nt store pointers but make a copy of the data you get as arguments.

The exception to this are container objects like arrays, maps, list or trees.

Regards, Alex
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Thanks all for the help in this.  Splitting the points seemed the fairest thing I could do.

:)
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now