const qualifier 释义 : const修饰符
- const
- constexpr
- const_cast
通过将变量的类型定义为const,可以使变量不可变:
const int bufSize = 512; // input buffer size
bufSize = 512; // error: attempt to write to const object
Because we can’t change the value of a const object after we create it, it must be
initialized.
const int i = get_size(); // ok: initialized at run time
const int j = 42; // ok: initialized at compile time
const int k; // error: k is uninitialized const
推翻自己旧的错误知识,非const入参不能接收const入参,const入参可以接收const和非const入参,这个以前的知识很粗糙,不精确且错误
- 函数参数中非const对象参数,可以兼容调用const及非const对象参数
- 函数参数中只有const指针参数,可以兼容调用const及非const指针参数,非const指针参数形参与const指针实参不兼容
- 函数参数中只有const引用参数,可以兼容调用const及非const引用参数,非const引用参数形参与const引用实参不兼容
#include <iostream>
using namespace std;
class B {
public:
B(int d) :data_(d) {}
int data() const{
return this->data_;
}
private:
int data_;
};
class A {
public:
void func(B a) {
std::cout << a.data() << std::endl;
}
void func_pointer(B* b) {
std::cout << b->data() << std::endl;
}
// const Object only call const function
void const_func_pointer(const B* b) {
std::cout << b->data() << std::endl;
}
void func_reference(B& b) {
std::cout << b.data() << std::endl;
}
// const Object only call const function
void const_func_reference(const B& b) {
std::cout << b.data() << std::endl;
}
void work() {
const B b(23);
func(b);
const B* a = new B(2);
// 编译报错:非const指针不能兼容调用const指针
func_pointer(a);
const B& c = B(4);
// 编译报错:非const引用不能兼容调用const引用
func_reference(c);
// const指针可以兼容调用const及非const指针
const B* d = new B(1);
const_func_pointer(d);
B* e = new B(3);
const_func_pointer(e);
// const引用可以兼容调用const及非const引用
const B* f = new B(6);
const_func_pointer(f);
const B& g = (8);
const_func_reference(g);
}
};
int main()
{
int i = 42;
const int ci = i;
int j = ci;
A a;
a.work();
return 0;
}
为了实现使const对象能在不同的文件中访问,需要在cpp文件中通过关键字extern声明const对象并赋初值,然后在其他使用的地方extern const声明然后使用
// file_1.cc defines and initializes a const that is accessible to other files
extern const int bufSize = fcn();
// file_1.h
extern const int bufSize; // same bufSize as defined in file_1.cc
const引用
- const引用只能赋值const对象
- const引用不能重新赋值其他值
- const引用能接收const及非const变量值
const int ci = 1024;
const int &r1 = ci; // ok: both reference and underlying object are const
r1 = 42; // error: r1 is a reference to const
int &r2 = ci; // error: nonconst reference to a const object
int i = 42;
const int &r1 = i; // we can bind a const int& to a plain int object
const指针
const double pi = 3.14; // pi is const; its value may not be changed
double *ptr = π // error: ptr is a plain pointer
const double *cptr = π // ok: cptr may point to a double that is const
*cptr = 42; // error: cannot assign to *cptr
顶层const:指针自己本身是const指针
底层const:指针指向的对象是const
int i = 0;
int *const p1 = &i; // we can’t change the value of p1; const is top-level
const int ci = 42; // we cannot change ci; const is top-level
const int *p2 = &ci; // we can change p2; const is low-level
const int *const p3 = p2; // right-most const is top-level, left-most is not
const int &r = ci; // const in reference types is always low-level
As we’ve seen,a pointer is an object that can point to a different object. As a
result,we can talk indendently about whether a pointer is const and whether the objects
to which it can point are const.
- we use the term top-level const to indicate that the pointer itself is a const.
- when a pointer can point to a const object,we refer to that const as a low-level const.
- top-level const appear in the built-in arithemetic types,a class type,a pointer type.
- low-level const appear in the base type of compound types such as pointers or references.
int i = 0;
int* const pi = &i; // we can't change the value of p1; const is top-level
const int ci = 42; // we can't change ci; const is top-level
const int* p2 = &ci; // we can change p2; const is low-level
const int* const p3 = p2; // right-most const is top-level,left-most is not
const int& r = ci; // const reference types is always low-level
// the distinction between top-level and low-level matters when we copy an object.
// when we copy an object ,top-level consts are ignored.
i = ci; // ok: copying the value of ci; top-level const in ci is ignored
p2 = p3; // ok: pointed-to type matches; top-level const in p3 is ignored
// when we copy an object,both objects must have the same low-level const qualification
// or there must be a conversion between the types of the two objects. in General ,we
// can convert a nonconst to const but not the other way round.
int* p = p3; // error: p3 has a low-level const but p does't
p2 = p3; // ok:p2 has the same low-level const qulification as p3
p2 = &i; // ok: we can convert int* to const int*
int& r = ci; // error: can't bind an ordinary int& to a const int object,新知识:之前真不知道这个
const int& r2 = i; // ok: can bind const int& to plain int
// 新标准中:用constexpr定义变量来初始化constant的表达式,或者constexpr functions
constexpr int mf = 20; // 20 is a constant expression
constexpr int limit = mf + 1; // mf + 1 is a constant expression
constexpr int sz = size(); // ok only if size is a constexpr function
// 新标准中容易让人迷惑的知识:constexpr指针的声明,constexptr作用于指针本身,而不是指针指向的数据类型
const int* p = nullptr; // p is pointer to a const int
constexpr int *q = nullptr; // 有点反人类,其实等效于: int const *q = nullptr
// 新标准细节中的细节:const int i = 42; constexpr int i = 42 本质一样,但是有细节不一样
const int& i = 43; // here const is a top-level const(const indicator to pointer itself)
constexpr int& i = 43; // here const is a top-level const ,as same the :int& const i = 43;
constexpr int j = 5;
constexpr const int *p = &j; // p is a constant pointer to the const int i
constexpr int *p1 = &j; // p1 is a constant pointer to the int j
int i = 0, & r = i;
auto a = r; // a is an int (r is an alias for i,which has type int)
const int ci = i, & cr = ci;
auto b = ci; // b is an int(top-level const in ci is dropped)
auto c = cr; // c is an int (cr is an alias for ci whose const is top-level)
auto d = &i; // d is an int* (& of an int object is int*),top-level pointer is ignored
auto e = &ci; // e is const int* (& of a const object is low-level const)
const auto f = ci; // deduced type of ci is int; f has type const int
auto& g = ci; // g is a const int& that is bound to ci
// auto 里的const修饰要显示在auto前添加,如 const auto,否则
// auto 不能自动推到声明为 const auto&
auto& h = 42; // error: we can't bind a plain refrence to a literal,
const auto& j = 42; // ok: we can bind a const reference to a literal
auto k = ci, & l = i; // k is int; l is int&
auto& m = ci, * p = &ci; // m is a const int&; p is a pointer to const int
// 纠正自己以前的错误知识:
// 1. auto变量可以推导const及nonconst的初始值
// 2. auto引用不能推导const引用的的初始值
// 3. auto指针不能推导const指针的初始值
// 4. 非常量引用的初始值必须为左值
auto& n = i, * p2 = &ci; // error: type deduced from i is int,type deduced from &ci is const int
decltype : 类型获取关键字
decltype获取变量的类型的时候,会将返回变量的top-level const
// decltype 获取表达式或函数返回值的不确定类型
decltype(f()) sum = x;
const int ci = 0, & cj = ci;
decltype(ci) x = 0; // y has type const int
decltype(cj) y = x; // y has type const int& is bound to x
decltype(cj) z; // error: z is a reference and must be initialized
问精通C++的程序员一个问题,你了解const关键字吗?
- const不允许变量值改变,const变量必须定义时赋初值
- const pointer和const reference的top-level,low-level在copy object的差异
- consttant expression的值计算是发生在编译器
- constexpr变量的定义是top-level,违反人第一直觉
vector<int>::const_iterator it3; // it3 can read but not write elements
vector<int> v;
auto it3 = v.cbegin(); // it3 has type vector<int>::const_iterator
const char ca1[] = "A String example";
const char ca2[] = "A different string";
if( ca1 < ca2 ) // undefined: compare two unrelated addresses
if(strcmp(ca1,ca2) < 0) // same effect as string comparison s1 < s2
char * str = s;
const char* str = s.c_str(); // ok
for(const auto& item : list)
const_cast
const char* pc;
char* p = const_cast<char*>(pc); // ok: but writing througn p is eperand
const int ci = 42; // we cannot change ci; const is top-level
int i = ci; // ok: when we copy ci,its top-level const is ignored
int* const p = &i; // const is top-level,can't assign up to p
*p = 0; // ok:change through p are allowed; i is now 0
Introducing const Member Functions
The purpose of that const is to modify the type of the implicit this pointer.
By default,the type of this is a const pointer to the nonconst version of the class type. // nonconst class_type const * this;
so by default ,we cannot bind this to a const object.
this fact,in turn ,means that we cannot call an ordinary member function on a const object.
在类中,默认this指针是一个const指针,指向非const的类的类型
所以基于非const low-level不能绑定到const low-level的指针类型,默认类中的this指针不能绑定到const类对象
所以,我们的const对象不能调用普通成员函数。
万物有因果,const成员函数是为了解决让默认的this指针指向的数据类型为const,但是由于成员函数参数列表中this是隐式的,所以加在了成员函数括号后面
所以const类型的成员函数实现了让默认的this指针指向的类的类型从默认的nonconst变成const class type
// pseudo-code illustration of how the implicit this pointer is used
// this code is illegal: we may not explicitly define the this pointer ourselves
// not that this is a pointer to const because isbn is a const member
std::string Sales_data::isbn(const Sales_data* const this){
return this->isbn;
}
对于const关键字,我个人认为做重要的一个知识点,它出现了:
- const成员函数的第一个作用是改变该成员函数里的默认this指针指向的类型,从非const类变为const类,所以const成员函数函数体不能改变类的成员变量
- 将成员函数声明为const提高了灵活性,因为默认的this指针是指向非const的const指针,所以对于const对象的引用或指针来说,无法调用const成员函数
如果将成员函数声明为const,将使得const类对象或非const类对象的引用或指针都能调用该成员函数 - 很多c++程序员,包括中高级程序员,不知道第二条的灵活性,就理直气壮地认为成员函数声明为const是个鸡肋,我的类不存在需要声明为const类类型,这解释很无耻又卑鄙
.h头文件和.cc实现文件里的类成员函数的声明必须一致
double Sales_data::avg_price() const{
if(units_sold){
return revenue / units_sold;
}
else
eturn 0;
}
这个代码跟const联系比较小,但是有意思
// input transactions contain ISBN,number of copies sold,and sales price
istream& read (istream& is,Sales_data& item){
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
ostream& print(ostream& os,const Sales_data& item){
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
Friends
class Sales_data{
friend Sales_data add(const Sales_data&,const Sales_data&);
}
*Returning this from a const Member Function
默认的成员函数返回*this的是非const类型,所以const成员函数返回的是this指针指向const的类型
class Screen{
public:
Screen &set(char c){
contents[cursor] = c;
return *this;
}
const Screen* display(std::cout& c,const Screen& s) const{
return *this;
}
}
Screen myScreen;
// if display returns a const reference, the call to set is an error
myScreen.display(cout).set('*');
非const成员函数的调用和const成员函数的调用
class Screen {
public:
// display overloaded on whether the object is const or not
Screen& display(std::ostream& os)
{
do_display(os); return *this;
}
const Screen& display(std::ostream& os) const
{
do_display(os); return *this;
}
private:
// function to do the work of displaying a Screen
void do_display(std::ostream& os) const { os << contents; }
// other members as before
};
// call
Screen myScreen(5,3);
const Screen blank(5, 3);
myScreen.set(’#’).display(cout); // calls nonconst version
blank.display(cout); // calls const version
必须使用构造函数初始化列表为const、reference或没有默认构造函数的类类型的成员提供值。
class ConstRef {
public:
ConstRef(int ii);
private:
int i;
const int ci;
int& ri;
};
// error: ci and ri must be initialized
ConstRef::ConstRef(int ii)
{ // assignments:
i = ii; // ok
ci = ii; // error: cannot assign to a const
ri = i; // error: ri was never initialized
}
// ok: explicitly initialize reference and const members
ConstRef::ConstRef(int ii) : i(ii), ci(ii), ri(i) { }
Dynamically Allocated const Objects
// allocate and initialize a const int
const int* pci = new const int(1023);
//allocate a default-initialized const empty string
const string* pcs = new const string;
delete pci; // ok: deletes a const object
string* const p = new string[n];
拷贝构造函数
class Foo{
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&);
};
C++高级工程师可以了解的内容
class Foo {
public:
Foo sorted()&&; // may run on modifiable rvalues
Foo sorted() const&; // may run on any kind of Foo
// other members of Foo
private:
vector<int> data;
};
== 和 != 运算符的重载操作
bool operator==(const Sales_data& lhs, const Sales_data& rhs)
{
return lhs.isbn() == rhs.isbn() &&
lhs.units_sold == rhs.units_sold &&
lhs.revenue == rhs.revenue;
}
bool operator!=(const Sales_data& lhs, const Sales_data& rhs)
{
return !(lhs == rhs);
}
Member Access Operators
class StrBlobPtr {
public:
// prefix: return a reference to the incremented/decremented object
StrBlobPtr& StrBlobPtr::operator++()
{
// if curr already points past the end of the container, can’t increment it
check(curr, "increment past end of StrBlobPtr");
++curr; // advance the current state
return *this;
}
StrBlobPtr& StrBlobPtr::operator--()
{
// if curr is zero, decrementing it will yield an invalid subscript
--curr; // move the current state back one element
check(curr, "decrement past begin of StrBlobPtr");
return *this;
}
// increment and decrement
StrBlobPtr operator++(int) // postfix operators
{
// no check needed here; the call to prefix increment will do the check
StrBlobPtr ret = *this; // save the current value
++*this; // advance one element; prefix ++ checks the increment
return ret; // return the saved state
}
StrBlobPtr operator--(int) {
// no check needed here; the call to prefix decrement will do the check
StrBlobPtr ret = *this; // save the current value
--*this; // move backward one element; prefix -- checks the decrement
return ret; // return the saved state
}
// other members as before
std::string& operator*() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr]; // (*p) is the vector to which this object points
}
std::string* operator->() const
{ // delegate the real work to the dereference operator
return &this->operator*();
}
// other members as before
};
The Query Class
// interface class to manage the Query_base inheritance hierarchy
class Query {
// these operators need access to the shared_ptr constructor
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
};
Pack Expansion
template <typename T, typename... Args>
ostream&
print(ostream& os, const T& t, const Args&... rest)// expand Args
{
os << t << ", ";
return print(os, rest...); // expand rest
}
// call
print(cout, i, s, 42); // two parameters in the pack
继承树中的const类型成员函数重载
struct Base1 {
void print(int) const; // public by default
protected:
int ival;
double dval;
char cval;
private:
int* id;
};
struct Base2 {
void print(double) const; // public by default
protected:
double fval;
private:
double dval;
};
struct Derived : public Base1 {
void print(std::string) const; // public by default
protected:
std::string sval;
double dval;
};
struct MI : public Derived, public Base2 {
void print(std::vector<double>); // public by default
protected:
int* ival;
std::vector<double> dvec;
};
断断续续干了三天才把c++ primer里的所有相关const整理并写代码调试完