Functions of const in C ++

Source: Internet
Author: User

The first impression given by const is to define constants.

(1) const is used to define constants.

Example: const int n = 100; const int M = 200;
In this way, as long as N and m are used in the program, they represent integer 100, 200, and N and M as constants, which cannot be changed in the program.
But some people say that he never uses const to define constants during programming. I believe. But he does not know the real programming art. Using const to define constants not only facilitates programming but also improves program clarity. Are you willing to see 100 and 200 in the program, or are you willing to see only simple and clear n and M. I believe there is no benefit to your understanding.
It is also said that he does not need to use const to define constants. He uses the # define macro to define constants. Yes. But I wonder if you have found that sometimes the # define macro does not define constants as you wish. Next we will compare const and # define.
1.
(A) const defines constants as data types:
In this way, the constant compiler defined by const can perform a security check on the static data type, while # The constant defined by the define macro only performs a simple replacement of characters without a type security check, and sometimes there is a marginal effect (not as good as what you want ). An example of the so-called marginal effect is as follows:
# Define n 100
# Define m 200 + n
When M * n is used in the program, the original 100*(200 + n) is changed to 100*200 + N.
(B) # The define macro does not define constants. # Define <macro name> <string>. A string can be a constant, expression, or format string. When the program is compiled, replace the specified string in the macro name before compiling.
2.
Some debugging programs can debug const, but do not debug # define.
3.
When defining a local variable, the const scope is limited to the function body that defines the local variable. However, when # define is used, its scope is not limited to the function body that defines local variables, but from the definition point to the end point of the entire process order. However, you can also use # UNDEF to cancel its definition to limit its scope. Defining constants only with const does not play a powerful role. Const can also modify the form parameters, return values, and member functions of the class. This improves the robustness of functions. This is because the content modified by const can be forcibly protected by the static security check mechanism of C/C ++ to prevent accidental modification.

(2) const modifier function form parameters

Format parameters include input parameters and output parameters. When a parameter is used for output, const cannot be added, which will cause the function to lose the output function. Because the const modifier cannot be changed.
Const can only be used to modify input parameters.
Const can only be used to modify input parameters. First, let's talk about the three transfer methods of C ++ functions.
C ++ functions can be passed through values, pointers, and references. For more information, see other documents.
Value transfer:
Void fun (int x ){
X + = 5; // only y copies X in the stack, and X is only a copy of Y, A temporary space reopened in the memory sends the value of Y to X, which increases the running time and reduces the program efficiency.
}
Void main (void ){
Int y = 0;
Fun (y );
Cout </"Y =/" <Y <Endl; // y = 0;
}
Pointer transmission:
Void fun (int * X ){
* X + = 5; // modify the memory unit value pointed to by X.
}
Void main (void ){
Int y = 0;
Fun (& Y );
Cout </"Y =/" <Y <Endl; // y = 5;
}
Reference transfer:
Void fun (Int & X ){
X + = 5; // modify the object Value referenced by X & X = y;
}
Void main (void ){
Int y = 0;
Fun (y );
Cout </"Y =/" <Y <Endl; // y = 5;
}
After reading the transfer method, we will continue to talk about the situation that "const can only be used to modify input parameters.

When the input parameter uses the "value transfer" method, we do not need to add the const modifier, because when the value is passed, the function will automatically use the actual parameter copy initialization form parameter, when the form parameter is changed in the function body, only the copy on the stack is changed, not the actual parameter.
Note that when the input parameter is ADT/UDT (user-defined type and abstract data type), you should change "value transfer" to "const & pass ", to improve efficiency.
For example:
Void fun (A); // efficiency. A temporary object of type A generated in the function is used to copy parameter A, but the temporary object
// Construction, replication, and destructor all consume time.
Void fun (a const & A); // improves efficiency. You do not need to generate a temporary object when using "reference transfer", saving
// The time consumed by the construction, replication, and destructor processes. However, the reference may change a, so the const is added.

When the input parameter uses the "pointer transmission" method, adding the const modifier can prevent accidental modification of the memory unit pointed to by the pointer and play a protective role.
For example:
Void funstrcopy (char * strdest, const char * strsrc) // any memory unit that changes the strsrc point,
// The Compiler reports an error.
Some time, it protects the memory unit of the pointer, and can also protect the pointer itself to prevent its address from changing.
For example:
Void funstrcopy (char * strdest, const char * const strsrc)

(3) the return value of the const modifier Function

For example, if you add const to the return value of the function for "pointer passing", the return value cannot be directly modified, and the return value can only be assigned to the same type of pointer with const modification.
For example:
Const char * getchar (void ){};
Char * Ch = getchar (); // error const char * Ch = getchar (); // correct

(4) const modifier class member functions (Function Definition body)

The const modifier is applied to any function that does not modify data members. Therefore, when the data member is accidentally modified or the non-const member function is called, the compiler reports an error.
The const modifier class's member function form is: int getcount (void) const;
(5) Replace the pass value with the pass reference to the const
C ++ passes an object into or out of a function (a feature inherited from C) by passing a value ). Unless you specify other methods, the function parameters will be initialized by copying the actual parameters (actual argument), and the function caller will receive a copy of the function return value. This copy is generated by the object copy constructor. This makes pass-by-value a costly operation. For example, consider the following class hierarchy:

Class person {
Public:
Person (); // parameters omitted for simplicity
Virtual ~ Person (); // see item 7 for why this is virtual
...

PRIVATE:
STD: string name;
STD: String address;
};

Class student: public person {
Public:
Student (); // parameters again omitted
~ Student ();
...

PRIVATE:
STD: String schoolname;
STD: String schooladdress;
};

Now, consider the following code. Here we call a function -- validatestudent, which gets a student parameter (by passing the value) and returns whether it verifies the valid result:

Bool validatestudent s; // function taking a student
// By Value

Student Plato; // Plato studied under Socrates

Bool platoisok = validatestudent (Plato); // call the Function

What will happen when this function is called?

Obviously, student's copy constructor is called and Plato is used to initialize the parameter S. Similarly, when validatestudent returns, s will be destroyed. Therefore, the parameter transfer cost of this function is the call of a copy constructor of student and the call of a destructor of student.

But this is not all. A student object contains two string objects. Therefore, each time you construct a student object, you must also construct two string objects. A student object must be inherited from a person object. Therefore, each time you construct a student object, you must also construct a person object. A person object contains two additional string objects. Therefore, the structure of each person also bears the construction of the other two strings. In the end, the consequence of passing a student object by passing the value is to call the student copy constructor and the person copy constructor, and four string copy constructor calls. When a copy of the student object is destroyed, each constructor call corresponds to a constructor call, therefore, the full cost of passing a student by passing values is six constructors and six destructor!

Well, this is correct and worthwhile. After all, you want all your objects to be reliably initialized and destroyed. Even so, if there is a way to bypass all these constructor and destructor, it should be better: Pass the reference to const (pass by reference-to-const ):

Bool validatestudent (const student & S );

This is very effective: No constructor or destructor are called because no new object is constructed. The const in the modified parameter Declaration is very important. The original version of validatestudent accepts a student value parameter, so the caller knows that they shield the function from any possible changes to the student they pass in; validatestudent can only change one copy of it. Now student is passed as a reference and declared as a const. Otherwise, the caller must worry that validatestudent has changed the student they passed in.

Passing parameters by reference also avoids the slicing problem ). When a derived class object is passed as a base class Object (value passing method), the copy constructor of the base class is called, the special features that make the object behave like a derived class object are "disconnected. You have only one pure base class object left-That's nothing to be surprised, because it was created by a base class constructor. This is almost not what you want. For example, suppose you work on a group of classes that implement a graph Window System:

Class window {
Public:
...
STD: string name () const; // return name of window
Virtual void display () const; // draw window and Contents
};

Class required wwithscrollbars: public window {
Public:
...
Virtual void display () const;
};

All window objects have a name. You can get it through the name function, and all windows can be displayed. You can call the display function to do this. The fact that the display is virtual clearly tells you that the display method of a pure base class window object may be different from the display method of a dedicated javaswwithscrollbars object.

Now, suppose you want to write a function to print the name of a window and then display the window. The following function is incorrectly written:

Void printnameanddisplay (window W) // incorrect! Parameter
{
// May be sliced!
STD: cout <W. Name ();
W. Display ();
}

Consider what will happen when you call this function using a javaswwithscrollbars object:

Descriwwithscrollbars wwsb;

Printnameanddisplay (wwsb );

The parameter W will be constructed as a window object -- it is passed, remember? In addition, the special information of the wwsb object is cut off. In printnameanddisplay, regardless of the type of the object passed to the function, W will always behave like a window object (because it is a window object ). In particular, the call to display in printnameanddisplay always calls window: display. It will never be called during wwithscrollbars: display.

The method to bypass the disconnection problem is to pass W by referencing it to the const:

Void printnameanddisplay (const window & W) // fine, parameter won't
{
// Be sliced
STD: cout <W. Name ();
W. Display ();
}

Now w will look like the window actually passed in.

If you open the header of the compiler and peek at it, you will find that using a pointer to implement reference is a very typical practice, so passing something by reference usually means passing a pointer. It can be concluded that if you have an object of built-in type (for example, an int), passing it by value is often more efficient than passing the reference method. Therefore, for built-in types, when you need to make a selection between passing values and passing references to the const, there is no reason not to choose to pass values. The same recommendation applies to iterators and function objects in STL, because, as a convention, they are designed for passing values. The implementation of iterators and function objects has the responsibility to ensure efficient copying and not to be affected by the disconnection problem. (This is an instance of "how rules change, depending on which part of C ++ you use .)

The built-in types are small, so someone determines that all the small types are waiting for the selected values, even if they are user-defined. Such inferences are unreliable. Because an object is small, it does not mean that calling its copy constructor is cheap. Many objects-most STL containers in them-are the same as pointers, but copying such objects must copy everything they point to at the same time. It may be very expensive.

Even if a small object has a cheap copy constructor, there will be performance problems. Some compilers do not treat internal and user-defined types equally, even if they have the same underlying representation. For example, some compilers refuse to put an object consisting of only one double into a register, even if they prefer to put a pure double in general. If this happens, it is better to pass such an object as a reference, because the compiler will naturally put a pointer (referenced implementation) into a register.

A small user-defined type is not necessarily another reason for waiting for the selected value to be passed: as a user-defined type, its size often changes. A smaller type may become larger in future versions because its internal implementation may change. Even when you change to a different C ++ implementation, things may change. For example, when I write in this way, the implementation of the string type in some standard libraries is seven times the size of other implementations.

Generally, you can reasonably assume that only the built-in type and the iterator and function object type in STL are supported for low-cost data transfer. For any other type, follow the suggestions of this item and use the reference to replace the value.

Things to remember

· Replace the passed value with the passed reference to the const. In typical cases, it is more efficient and can avoid disconnection problems.

· This rule does not apply to built-in types and the iterator and function object types in STL. For them, passing values is usually more appropriate.

This document is only for summary. It may not be professional or comprehensive. Please advise.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.