C++练习Day3 C++语言联邦 const修饰词 对象初始化

Day3

C++是个多重范型编程语言,我们可以将C++视为多个次语言组成的联邦
主要的次语言如下:

C

C语言有局限:没有模板 没有异常 没有重载

Object-Oriented C++

封装 继承 多态 virtual函数

Template C++

C++泛型编程

STL

当我们做出

#define ASPECT_RATIO 1.653

记号名称ASPECT_RATIO可能无法被编译器看见,解决之道是以一个常量替代宏

const double AspectRatio=1.653;

有两种特殊情况值得一提
第一种是定义常量指针,由于常量定义式常常被放在头文件中(方便被不同的源码含入),有必要将指针声明为const(非指针所指之物)
第二是class专属常量,为了将常量的作用域限制于class内,就必须让常量成为class的成员,而为保证常量至多一份实体,则必须让其成为一个static成员:

class GamePlayer{
private:
	static const int NumTurns=5;//常量声明式
	int scores[NumTurns]//使用该常量

通常类似说c++需要为使用的任何东西提供定义式,但如上面代码所示,它是个class专属常量又是static且为整数类型,则需特殊处理。只要不取地址,就可以声明它而无需提供定义式。若必须有定义式,则另外提供如下代码:

const int GamePlayer::NumTurns;//放入实现文件,不再设初值

enum类型

一个属于枚举类型的数值可权充ints被使用

class GamePlayer{
private:
	enum{NumTurns=5};//令NumTurns成为5的一个记号名称
	int scores[NumTurns];
	...
};

如果你不想让其他人获得一个指针指向你的常量,就可以使用enum实现约束
它是模板元编程的基础技术

template inline函数

inline函数,即内联函数,内嵌函数
是将函数代码放入符号调用表,编译时直接展开,是一种以空间换时间的做法

template<typename T>
inline void callWithMax(const T&a,const T&b)
{
	f(a>b?a:b);
}

这个template产出一群函数。callWithMax是一个真正的函数,遵守作用域和访问规则

有了这三个帮助,我们对预处理器的需求降低了
对于常量,我们尽量以const或者enum替换#defines
对于形似函数的宏,最好用inline函数替换#defines

尽可能使用const

关于const,我们可以在class外部修饰global或者namespace作用域中的常量,或者用来修饰函数、文件中被声明为static的对象。我们还可以用它修饰class内部的static和non-static成员变量
对于指针,指针自身和指针所指物都可以声明为const

char greeting[]="Hello";
char* p=greeting;
const char* p=greeting;//指针所指物为const,指针非const
char* const p=greeting;//指针为const
const char* const p=greeting;//两者都是const 

注意:
const widget* pw与widget const* pw意义相同

const最具威力的用法是面对函数声明时的应用,它可以和函数的返回值、参数和函数自身(成员函数)产生关联
令函数返回一个常量值,往往能够降低因客户错误而造成的意外,又不至于放弃安全性和高效性
如有理数的operator*声明式:

class Rational{...};
const Rational operator*(const Rational& lhs,const Rational& rhs);

为什么要这样声明函数?当我们判断等式a*bc时,我们经常无意间犯下将“”写为“=”的错误,导致进行了赋值操作,有可能曹成严重的后果,如果我们将返回类型定义为const可避免这种低级错误。
const可实施于成员函数,这是为了确认该成员函数可作用于const对象上。这类成员函数使class接口比较容易理解,因为可以得知哪个函数可以改动对象内容而哪个对象不可以,并且它们使操作“const对象”成为可能
若两个成员函数只是在常量性有所不同,那么也可以重载,示例如下:

class TextBlock{
public:
	...
	const char& operator[](std::size_t position)const
	{return text[position];}
	char& operator[](std::size_t position)
	{return text[position];}
private:
	std::string text;
};

可以看到我们在上述代码中定义了两次operator[]函数

TextBlock tb("Hello");
std::cout<<tb[0];//调用的是non-const的operator函数
tb[0]='x';//写一个non-const TextBlock
const TextBlock ctb("World");
std::cout<<ctb[0];//调用的是const的operator函数
ctb[0]='x';//错误!不能写一个const TextBlock

确定对象在使用前被初始化

读取未初始化的对象会导致不明确行为,针对这个问题,最佳处理方法是:永远在对象使用之前将其初始化
对于内置类型,必须手工进行初始化

int x=0;
const char* text="A C-style String";

double d;
std::cin>>d;//读取input stream完成初始化

对于其他类型,初始化的责任在构造函数
赋值操作和初始化操作有很大的差别,考虑一个表现通讯录形式的class

class PhoneNumber{...};
class ABEntry{
public:
	ABEntry(const std::string name,const  std::string& address,
			const std::list<PhoneNumber>& phones);
private:
	std::string theName;
	std::string theAddress;
	std::list<PhoneNumber> thePhones;
	int numTimesConsulted;
};
ABEntry::ABEntry(const std::string name,const  std::string& address,
				 const std::list<PhoneNumber>& phones);
{
	theName=name;//赋值,而非初始化
	theAddress=address;
	thePhones=phones;
	numTimesConsulted=0;
}

C++规定,对象的成员变量的初始化发生在进入构造函数本体之前,ABEntry函数的更好的写法使使用成员初值列替换赋值动作

ABEntry::ABEntry(const std::string name,const  std::string& address,
				 const std::list<PhoneNumber>& phones);
	:theName(name),
	 theAddress(address),
	 thePhones(phones),
	 numTimesConsulted(0)
{}//构造函数本体不必有任何操作

若使用赋值方法,default构造函数的一切就浪费了
总是在初值列中列出所有成员变量
如果成员变量是const或是references,它们就一定需要初值,不能被赋值

C++有着十分固定的初始化次序,先是基类base class被初始化,然后是派生类derived class,而class的成员变量总是以声明次序被初始化。初始化array时需要指定大小,因此代表大小的成员变量必须先有初值
下面有些不太懂
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值