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 memberset_day(12); // ERROR calls non-const memberreturn 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(); // OKy(); // Error: ambiguousXLib::y(); // OK, resolves to XLibz(); // 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 origvoid 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();
}