C++ Primer : const 很容易被忽略,却极为细粒的syntax

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 = &pi; // error: ptr is a plain pointer
const double *cptr = &pi; // 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.

  1. we use the term top-level const to indicate that the pointer itself is a const.
  2. 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关键字吗?

  1. const不允许变量值改变,const变量必须定义时赋初值
  2. const pointer和const reference的top-level,low-level在copy object的差异
  3. consttant expression的值计算是发生在编译器
  4. 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关键字,我个人认为做重要的一个知识点,它出现了:

  1. const成员函数的第一个作用是改变该成员函数里的默认this指针指向的类型,从非const类变为const类,所以const成员函数函数体不能改变类的成员变量
  2. 将成员函数声明为const提高了灵活性,因为默认的this指针是指向非const的const指针,所以对于const对象的引用或指针来说,无法调用const成员函数
    如果将成员函数声明为const,将使得const类对象或非const类对象的引用或指针都能调用该成员函数
  3. 很多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整理并写代码调试完

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值