《Effective C++》笔记(一)------ 让自己习惯C++

本文深入探讨了C++编程中的关键技巧,包括视C++为一个语言联邦,使用const、enum、inline替代#define,以及强调const的正确使用。文章详细解释了如何通过成员初值列表初始化对象,确保对象在使用前已正确初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Item01:视C++为一个语言联邦
  • C
  • Object-Oriented C++
  • Template C++
  • STL
Item02:尽量以const、enum、inline替换#define
  • 以编译器替换预处理器,const、enum、inline都是在程序编译阶段完成;
#define ASPECT_RATIO 1.653
const double aspectratio = 1.653;
  • 宏的名称可能在编译前就被预处理器移走,并没有进入符号表,所以编译错误信息也可能仅仅提到1.653,而非ASPECT_RATIO;
  • 宏可能导致盲目出现多份目标代码,常量仅一次;
class GamePlayer
{
private:
	static const int NumTurns = 5;  //常量声明式
	int scores[NumTurns];           //使用常量
};
  • C++要求你对你所使用的任何一个东西提供定义式。如果他是一个class专属静态常量,且为整数类型(int、char、bool),只要不取他们的地址,可以仅声明不定义;但是要取某个class专属常量的地址,就必须进行如下定义:
const int GamePlayer::NumTurns;  //定义式,该式子应该放在实现文件中,由于class常量已经在声明时获得初值,定义时不可在设置初值。
//#define 无作用域,不能用来定义class专属常量,也不能提供封装性。
  • 若编译器不允许在class声明中给初值,但又必须在编译期间告诉数组大小,可以如下做法:
class GamePlayer2
{
private:
	enum { Numturn = 5 }; //令numturn成为5的符号名称
	int scores[Numturn];
};

结论:

1.对于单纯常量,最好以const对象或enums替换#define

2.对于函数宏,最好用inline函数替换#defines

Item03:尽可能使用const
  • const告诉编译器一个语义约束——“不能被改变”。
一、修饰

1.1 修饰global或namespace作用域中的常量

int main()
{
	const int m_i = 100;
	int* p = (int*)&m_i;
	*p = 200;
	cout << m_i << endl;
	return 0;
}

1.2 修饰指针

char happy[] = "Hello";

const char* p = happy;   //happy内容不可改变
char const* p = happy;   //happy内容不可改变,与上条等价
p[0] = 'L';  //×
++p;         //√

char* const p = happy;   //指针p本身不可改变
p[0] = 'L';  //√
++p;         //×

const char* const p = happy; //都不可改变
p[0] = 'L';  //×
++p;         //×
  • 若const出现在 * 左边,表示被指物是常量;若出现在* 右边表示指针本身是常量;
1.3 修饰迭代器
std::vector<int> m_vec;

const std::vector<int>::iterator iter = m_vec.begin();  //等价于T* const
*iter = 10;  //√
++iter;      //×

std::vector<int>::const_iterator iter = m_vec.begin();  //等价于const T*
*iter = 10;  //×
++iter;      //√

1.4 引用常量

可以有const指针,但是没有const引用(const引用可读不可改,与绑定对象是否为const无关)

**注:引用可以指向常量,也可以指向变量。例如int &a=b,使引用a指向变量b。而为了让引用指向常量,必须使用常量引用,如const int &a=1; 它代表的是引用a指向一个const int型,这个int型的值不能被改变,而不是引用a的指向不能被改变,因为引用的指向本来就是不可变的,无需加const声明。即指针存在常量指针int const p和指针常量int const p,而引用只存在常量引用int const &a,不存在引用常量int& const a。

const int& a = 12; //a只读
1.5 小tips:让函数返回一个常量值可避免意外
class Rational{};
const Rational& operator*(const Rational& rhs1, const Rational& rhs2);
Rational a,b,c;
if(a * b = c) ...
  • 为了防止if中的比较操作误写为赋值操作,我们有两种方法解决:
    • 1.让函数返回一个常量值,因为常量不可以被赋值;
    • 2.将if的条件中的常量写在==左边;
二、const成员函数

2.1 用const修饰成员函数的意义:

  • 1.得知那个函数可以改动对象,那个函数不行;
  • 2.让操作const对象成为可能;

2.2 bitwise constness & logical constness

2.n tips

  • 不可以同时用const和static修饰成员函数。

    • C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this* 。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时const的用法和static是冲突的。

    • 我们也可以这样理解:两者的语意是矛盾的。static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。因此不能同时用它们。

Item04:确定对象被使用前已被初始化
1.C/C++中到底哪些对象在声明时会被默认初始化?
2.构造函数
  • 永远在使用对象之前将其初始化,对于无任何成员的内置类型,必须手工初始化;
//内置类型
int x = 0;
const char* text = "A fww";

double d;
std::cin >> d; 
  • 内置类型之外的其他东西(类),初始化由构造函数完成,这里会存在一个赋值初始化的区分;
  • 以下操作未赋值而非初始化,它会先调用default构造函数然后调用拷贝赋值运算符,效率不高;
class PhoneNumber {...};
class Member
{
public:
    Member(const std::string& name, cosnt std::string& address, const std::list<PhoneNumber>& phones);
private:
    std::string name;
    std::string address;
    int numMember;
    std::list<PhoneNumber> thePhones;
  
};

Member::Member(const std::string& na, cosnt std::string& ad, const std::list<PhoneNumber>& phones)
{
    //这里是赋值而非初始化;
    name = na;
    address = ad;
    thePhones = phones;
    numMember = 50;
}
  • 更好的做法是使用 成员初值列,他只调用一次拷贝构造函数;
Member::Member(const std::string& na, cosnt std::string& ad, const std::list<PhoneNumber>& phones)
    :name(na),
    address(ad),
    thePhones(phones),
    numMember(50)  //成员初值列
{ }
  • 需要注意的是内置类型也要写入成员初值列中;
  • 当成员变量是const 或者 引用时,就一定需要初值,不能被赋值;
3.C++类中数据成员初始化顺序?

1.成员变量在使用初始化列表初始化时,与构造函数中初始化成员列表的顺序无关,只与定义成员变量的顺序有关。

2.如果不使用初始化列表初始化,在构造函数内初始化时,此时与成员变量在构造函数中的位置有关。

3.类中const成员常量必须在构造函数初始化列表中初始化。

4.类中static成员变量,只能在类内外初始化(同一类的所有实例共享静态成员变量)。

初始化顺序:

  • 1) 基类的静态变量或全局变量
  • 2) 派生类的静态变量或全局变量
  • 3) 基类的成员变量
  • 4) 派生类的成员变量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值