Const
• declares a variable to have a constant value
const int x = 123;
x = 27; // illegal!
x++; // illegal!
int y = x; // Ok, copy const to non-const
y = x; // Ok, same thing
const int z = y; // ok, const is safer
变量variable
常量 constant ,值不会发生变化的变量,如被const修饰的x
字面量 literal ,如整数123
Constants
• Constants are variables
– Observe scoping rules
– Declared with “const” type modifier
• A const in C++ defaults to internal linkage
– the compiler tries to avoid creating storage for a const -- holds the value in its symbol table.
– extern forces storage to be allocated
Compile time constants
const int bufsize = 1024;
• value must be initialized
• unless you make an explicit extern declaration:
extern const int bufsize;
• Compiler won't let you change it
• Compile time constants are entries in compiler symbol table, not really variables.
Run-time constants
• const value can be exploited
const int class_size = 12; 编译时常量
int finalGrade[class_size]; // ok
int x;
cin >> x;
const int size = x;
double classAverage[size]; // error! c++98是错的,不能用变量作为数组大小,c++11才对
Aggregates
• It’s possible to use const for aggregates, but storage will be allocated. In these situations, const means “a piece of storage that cannot be changed.
• However, the value cannot be used at compile time because the compiler is not required to know the contents of the storage at compile time.
const int i[] = { 1, 2, 3, 4 }; 数组元素不能修改
float f[i[3]]; // Illegal c++98标准,const变量不能做其他数组大小
struct S { int i, j; };
const S s[] = { { 1, 2 }, { 3, 4 } };
double d[s[1].j]; // Illegal
Pointers and const
• char * const q = "abc"; // q is const
*q = 'c'; // OK
q++; // ERROR
• const char *p = "ABCD"; // (*p) is a const char
*p = 'b'; // ERROR! (*p) is the const
Quiz: What do these mean?
string p1( “Fred" );
const string* p = &p1; //对象不能改
string const* p = &p1; //对象不能改
string *const p = &p1; //指针不能改
const在*之前说明对象不能不能改,在*后面表示指针不能改
Pointers and constants
Remember:
*ip = 54; // always legal since ip points to int
*cip = 54; // never legal since cip points to const int
ip = &ci错误,因为ci可能被*ip修改
cip = &ci正确,是因为cip指针不能修改对象,所以ci不会被修改,安全
String Literals
char* s = "Hello, world!";
• s is a pointer initialized to point to a string constant
• This is actually a const char* s but compiler accepts it without the const
• Don't try and change the character values (it is undefined behavior)
• If you want to change the string, put it in an array:
char s[] = "Hello, world!";
int main(){
char* s = "abc";
char a[] = "abc";
printf("s=%p\n", s);
printf("a=%p\n", a);
}
/*
输出:
s=0x103f41f9e 地址比a小
a=0x7ff7bbfc1334
a在栈内,s指向的内存在很小的地方
*/
int main(){
char* s = "abc";
char a[] = "abc";
printf("s=%p\n", s);
printf("a=%p\n", a);
s[0] = 'c';
pringf("1\n");
a[0] = 'c';
printf("2\n");
}
/*
s[0] = 'c'报错
*/
Conversions
• Can always treat a non-const value as const
void f(const int* x);
int a = 15;
f(&a); // ok
const int b = a;
f(&b); // ok
b = a + 1; // Error!
You cannot treat a constant object as non-constant without an explicit cast
(const_cast)
Passing by const value?
void f1(const int i) {
i++; // Illegal -- compile-time error
}
Returning by const value?
int f3() { return 1; }
const int f4() { return 1; }
int main() {
const int j = f3(); // Works fine
int k = f4(); // But this works fine too!
}
Passing and returning addresses
• Passing a whole object may cost you a lot. It is better to pass by a pointer. But it’s possible for the
programmer to take it and modify the original value.
• In fact, whenever you’re passing an address into a function, you should make it a const if at all possible.
• Example: ConstPointer.cpp, ConstReturning.cpp
const object
Constant objects
• What if an object is const?
const Currency the_raise(42, 38);对象里的任何一个成员变量都不能被修改
• What members can access the internals?
• How can the object be protected from change?
• Solution: declare member functions const
– Programmer declares member functions to be safe
Const member functions
• Cannot modify their objects
//不保证不修改,不是保证修改
int Date::set_day(int d){
//...error check d here...
day = d; // ok, non-const so can modify
}
//保证不修改成员变量,不会调用非const的成员函数
//因为调用了非const成员函数可能会修改成员变量
int Date::get_day() const {
day++; //ERROR modifies data member
set_day(12); // ERROR calls non-const member
return day; // ok
}
Const member function
• Repeat the const keyword in the definition as well as
the declaration 声明和定义都要标注const
int get_day () const;
int get_day() const { return day };
• Function members that do not modify data should be
declared const
• const member functions are safe for const objects
Const objects
• Const and non-const objects
// non-const object
Date when(1,1,2001); // not a const
int day = when.get_day(); // OK
when.set_day(13); // OK
// const object
const Date birthday(12,25,1994); // const
int day = birthday.get_day(); // OK
birthday.set_day(14); // ERROR
class A{
int i = 0;
public:
A(int li):i(li){}
int getV() const{return i;}
void setV(int v){i = v;}
};
int main(){
A a(10);
const A aa(11);
a.setV(12);
cout << aa.getV() << endl;
aa.setV(13);//报错,不能修改成员变量
}
class A{
int i = 0;
public:
A(int li):i(li){}
int f() const{
cout << "f() const" << endl;
reutrn i;
}
int f() const{
cout << "f()" << endl;
return i;
}
void setV(int v){i = v;}
};
int main(){
A a(10);
const A aa(11);
a.setV(12);
a.f();//调用无const的f()
aa.f();//调用有const的f()
}
//两个函数构成重载
//int f() const相当于int f(const A* this) const
//int f() 相当于int f(A* this)
//const函数指的是this是const
Constant in class
class A {
const int i;
};
• has to be initialized in initializer list of the constructor
//当i是const时
//不能用构造函数赋值
A(int x){
i = x;
}
//可以用初始化列表的方式给i设置初始化值
A(int x): i(x){}
Compile-time constants in classes
class HasArray {
const int size;
int array[size]; // ERROR!
};
//• use "anonymous enum" hack 匿名枚举基础
class HasArray {
enum { size = 100 };
int array[size]; // OK!
};
//• Make the const value static:
class HasArray{
static const int size = 100;//static指所有对象使用同一个变量
int array[size];
}
//·static indicates only one per class(not one per obeject)
Static
Static in C++
Two basic meanings
• Static storage
–allocated once at a fixed address
• Visibility of a name
• internal linkage
-
Don't use static except inside functions and
classes.
-
静态本地变量实际上是特殊的全局变量
-
它们位于相同的内存区域
-
静态本地变量具有全局的生存期,函数内的局部作用域
Uses of “static” in C++
Global static hidden in file
C语言规则,File 2编译通过,但链接时extern int s_local报错,因为在File1中属于static,只能在当前文件使用
Static inside functions
• Value is remembered for entire program
• Initialization occurs only once
• Example:
–count the number of times the function has been called
void f() {
static int num_calls = 0;
...
num_calls++;
}
Static applied to objects
• Suppose you have a class
class X {
X(int, int);
~X();
...
};
• And a function with a static X object
void f() {
static X my_X(10, 20);
...
}
class A{
private:
int i;
public:
A(int li):i(li){
cout << "A()" << i << endl;
}
~A(){
cout << "~A()" << i << endl;
}
};
A a(10);
int main(){
cout << "main()" << endl;
cout << "end of main()" << endl;
}
/*
输出:
A()10
main()
end of main()
~A()10
全局变量在main之前创建,main之后销毁
*/
class A{
private:
int i;
public:
A(int li):i(li){
cout << "A()" << i << endl;
}
~A(){
cout << "~A()" << i << endl;
}
};
A a(10);
void f(){
A aa(11);
}
int main(){
cout << "main()" << endl;
f();
cout << "end of main()" << endl;
}
/*
输出:
A()10
main()
A()11
~A()11
end of main()
~A()10
*/
如果f()是static
class A{
private:
int i;
public:
A(int li):i(li){
cout << "A()" << i << endl;
}
~A(){
cout << "~A()" << i << endl;
}
};
A a(10);
void f(){
static A aa(11);
}
int main(){
cout << "main()" << endl;
f();
cout << "-----" << endl;
f();
cout << "end of main()" << endl;
}
/*
输出:
A()10
main()
A()11
--------
end of main()
~A()11
~A()10
第一次调用f()时才构造aa,aa的析构发生在main函数结束,并且第二次调用函数时不会再构造aa
*/
Static applied to objects ...
• Construction occurs when definition is encountered
–Constructor called at-most once
–The constructor arguments must be satisfied
• Destruction takes place on exit from program
–Compiler assures LIFO order of destructors
Conditional construction
• Example: conditional construction
void f(int x) {
if (x > 10) {
static X my_X(x, x * 21);
...
}
•my_X
–is constructed once, if f() is ever called with x > 10
–retains its value
–destroyed only if constructed
Global objects
• Consider
#include "X.h"
X global_x(12, 34);
X global_x2(8, 16);
• Constructors are called before main() is entered
–Order controlled by appearance in file
– In this case, global_x before global_x2
– main() is no longer the first function called
• Destructors called when
–main() exits
–exit() is called
Static Initialization Dependency
• Order of construction within a file is known
• Order between files is unspecified!
• Problem when non-local static objects in different files
have dependencies.
• A non-local static object is:
–defined at global or namespace scope
–declared static in a class
–defined static at file scope
x2的构造需要x1,但两个在不同的文件,跨编译单元无法保证构造顺序
Static Initialization Solutions
• Just say no -- avoid non-local static dependencies.
• Put static object definitions in a single file in correct order.
Can we apply static to members?
• Static means
–Hidden
–Persistent
• Hidden: A static member is a member
–Obeys usual access rules
• Persistent: Independent of instances
• Static members are class-wide
–variables or
–functions
class A{
private:
int i;
static int s;
public:
A(int li):i(li){
s = 0;
cout << "A()" << i << endl;
}
~A(){
cout << "~A()" << i << endl;
}
void setS(int k){s = k;}
int getS() const{return s;}
};
int A::s = 0;//需要写这个定义,因为静态成员变量相当于全局变量,不写出现链接错误,但是定义前面不能写static,因为要在其他的文件使用A类。
int main(){
A a1(10);
A a1(20);
cout << a2,getS() << endl;
a1.setS(11);
cout << a2,getS() << endl;
}
/*
输出:
A()10
A()20
0
11 //a2打印s是11。说明静态成员变量在所有对象中共享,实际上是因为静态成员变量属于特殊的全局变量
~A()20
~A()10
*/
静态成员变量在.h的类中声明,一定要注意在.cpp中写定义
Static members
• Static member variables
–Global to all class member functions
–Initialized once, at file scope
–provide a place for this variable and init it in .cpp
–No ‘static’ in .cpp
• Example: StatMem.h, StatMem.cpp
• Static member functions
–Have no implicit receiver ("this")
• (why?)
–Can access only static member variables
• (or other globals)
–No ‘static’ in .cpp
–Can’t be dynamically overridden
• Example: StatFun.h, StatFun.cpp
To use static members
• <class name>::<static member>
• <object variable>.<static member>
NameSpace
Controlling names:
• Controlling names through scoping
• We’ve done this kind of name control:
class Marbles {
enum Colors { Blue, Red, Green };
...
};
class Candy {
enum Colors { Blue, Red, Green };
...
};
Avoiding name clashes
• Including duplicate names at global scope is a problem:
// old1.h
void f();
void g();
// old2.h
void f();
void g();
Avoiding name clashes (cont)
• Wrap declarations in namespaces.
// old1.h
namespace old1 {
void f();
void g();
}
// old2.h
namespace old2 {
void f();
void g();
}
Namespace
• Expresses a logical grouping of classes, functions, variables, etc.
• A namespace is a scope just like a class
• Preferred when only name encapsulation is
needed
namespace Math {
double abs(double );
double sqrt(double );
int trunc(double);
...
} // Note: No terminating end colon!
Defining namespaces
• Place namespaces in include files:
// Mylib.h
namespace MyLib {
void foo();
class Cat {
public:
void Meow();
};
}
Defining namespace functions
• Use normal scoping to implement functions in
namespaces.
// MyLib.cpp
#include ”MyLib.h”
void MyLib::foo() { cout << "foo\n"; }
void MyLib::Cat::Meow() { cout << "meow\n"; }
Using names from a namespace
• Use scope resolution to qualify names from a namespace.
• Can be tedious and distracting.
#include ”MyLib.h”
void main(){
MyLib::foo();
MyLib::Cat c;
c.Meow();
}
Using-Declarations
• Introduces a local synonym for name
• States in one place where a name comes
from.
• Eliminates redundant scope qualification:
void main() {
using MyLib::foo;
using MyLib::Cat;
foo();
Cat c;
c.Meow();
}
Using-Directives
• Makes all names from a namespace available.
• Can be used as a notational convenience.
void main() {
using namespace std;
using namespace MyLib;
foo();
Cat c;
c.Meow();
cout << “hello” << endl;
}
Ambiguities
• Using-directives may create potential ambiguities.
• Consider:
// Mylib.h
namespace XLib {
void x();
void y();
}
namespace YLib {
void y();
void z();
}
Ambiguities (cont)
• Using-directives only make the names
available.
• Ambiguities arise only when you make calls.
• Use scope resolution to resolve.
void main() {
using namespace XLib;
using namespace YLib;
x(); // OK
y(); // Error: ambiguous
XLib::y(); // OK, resolves to XLib
z(); // OK
}
Namespace aliases
• Namespace names that are too short may clash
• names that are too long are hard to work with
• Use aliasing to create workable names
• Aliasing can be used to version libraries.
namespace supercalifragilistic {
void f();
}
namespace short = supercalifragilistic;
short::f();
Namespace composition
• Compose new namespaces using names from other
ones.
• Using-declarations can resolve potential clashes.
• Explicitly defined functions take precedence.
namespace first {
void x();
void y();
}
namespace second {
void y();
void z();
}
namespace mine {
using namespace first;
using namespace second;
using first::y(); // resolve clashes to first::x()
void mystuff();
...
}
Namespace selection
• Compose namespaces by selecting a few
features from other namespaces.
• Choose only the names you want rather than
all.
• Changes to “orig” declaration become
reflected in “mine”.
namespace mine {
using orig::Cat; // use Cat class from orig
void x();
void y();
}
Namespaces are open
• Multiple namespace declarations add to the
same namespace.
–Namespace can be distributed across multiple files.
//header1.h
namespace X {
void f();
}
// header2.h
namespace X {
void g(); // X how has f() and g();
}