1、让自己习惯C++

条款01:视C++为一个语言联邦

C++可以理解为四个次语言的结合:

  • C。 基本的区块(blocks)、语句(statements)、预处理(preprocessor)、内置数据类型(build-in data types)、数组(arrays)、指针(pointers)等都来自于C
  • Object-oriented C++。这部分是C with Class部分,包括:封装、继承、多态等古典面向对象设计守则。
  • Template C++。这是C++泛型编程的部分,也是大多数程序员经验最少的部分。
  • STL。容器、迭代器、算法及函数对象的标准库。

总结:

  • C++高效编程守则视情况而变化,取决于你使用C++的哪一部分。



条款02:尽量以const,enum,inline替换#define

1)基本数据结构:

#define ASPECT_RATIO 1.653

替换为

const double AspectRatio = 1.653;

2) 常量指针

#define AUTHOR_NAME "Scott Meyers"

替换为

const char* const AuthorName = "Scott Meyes";

注意将指针和指针指向的内容都定义为const。

3)类成员常量

防止常量生成多份实体,将常量定义为static

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

4) 类成员常量使用enum

有些旧式编译器不支持static 型常量在class中声明时初始化,那么就需要用到enum的方式。

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

5)函数宏

#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) , (b))

看起来就复杂的不行,每个实参必须加上小括号。即使这种情况下也会有问题。
比如:

int a = 5, b = 0;
CALL_WITH_MAX(++a, b); // a被累加2次
CALL_WITH_MAX(++a, b+10); //a被累加3次

所以我们使用tempate inline函数解决这些问题。

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

总结:

  • 对于单纯变量,最好以const 对象或enums替换 #define
  • 对于形似函数的宏,最好改用template的inline函数替换#define



条款03:尽可能使用const

1)const指针的用法

const 出现在指针左边,则修饰指针指向的对象;若const 出现在指针右边,则修饰指针本身。

char greeting[] = "hello";
char* p = greeting; // no-const pointer, no-const data
const char* p = greeting; //no-const pointer, const data
char const* p = greeting; //no-const pointer, const data
char* const p = greeting; //const pointer, no-const data
const char* const p = greeting; // const pointer, const data

2) const 迭代器

const 修饰迭代器表明迭代器不得指向不同的东西,如果想要迭代器指向的东西不能改变,需要用const_iterator。

std::vecor<int> vec;
...
const std::vector<int>::iterator iter = vec.begin();
*iter = 10; //没问题,改变迭代器指向的内容
++iter; //错误, iter是const的

std::vector<int>::const_iterator cIter = vec.begin();
*cIter = 10; //错误, *cIter是const的 
++cIter; //没问题,改变cIter

3) const 修饰成员函数

原则上,const修饰成员函数,表明在该函数内不能改变本对象的任何数据。

class CTextBlock {
public:
	std::size_t length() const;
private:
	char* pText;
	std::size_t textLength;
	bool lengthIsValid;
};

std::size_t CTextBlock::length() const{
	if(!lengthIsValid){
		textLength = std::strlen(pText); //错误!在const成员函数内,不能赋值给textLength 和lengthIsValid
		lengthIsValid = true;
	}
	return textLength;
}

这种情况下,可以使用mutable关键词。表明成员变量即使在const 成员函数内也可能被更改

class CTextBlock {
public:
	std::size_t length() const;
private:
	char* pText;
	mutable std::size_t textLength;
	mutable bool lengthIsValid;
};

std::size_t CTextBlock::length() const{
	if(!lengthIsValid){
		textLength = std::strlen(pText); //正确
		lengthIsValid = true;
	}
	return textLength;
}

4)const和no-const成员函数避免重复

首先要知道:两个成员函数如果只是常量性不同,可以被重载。

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

TextBlock tb("Hello");
std::cout << tb[0];  //调用no-const TextBlock::operator[]

const TextBlock ctb("Hello");
std::cout << ctb[0]; //调用const TextBlock::operator[]




当const和no-const函数的内容一致,但是代码量庞大时,就会造成严重的代码重复。所以我们可以通过no-const版本调用const版本的方式来简化代码。

class TextBlock {
public:
	const char& operator[](std::size_t position) const{ //operator[] for const对象
		...    //边界检查
		...    //志记数据访问
		...    //检验数据完整性
		...
		return text[position];
	} 
	char& operator[](std::size_t position){
		const_cast<char&>(            //将operator[]返回值的const转除
			static_cast<const TextBlock&> (*this)[position]);   //将*this加上const属性,并调用const operator[]
		return text[position];
	}
private:
	std::string text;	
};

这个代码有两次强制类型转换。第一次是使用static_cast将*this加上const属性,以避免no-const operator[]一直重复调用自己。
第二次是使用const_cast强制从返回值移除了const属性。



总结:

  • 将某些东西声明为const可帮助编译器侦测出错误的用法。const可被施加于在任何作用域的对象、函数参数、函数返回类型、成员函数本体。
  • 编译器强制实施bitwise constness,但你在编写程序时应该使用"概念上的常量性"。
  • 当const和no-const成员函数有实质上的等价关系时,令no-const版本调用const版本可避免代码重复。


条款04:确定对象被使用前已先被初始化

1)内置型对象进行手工初始化

int x = 0;
const char* text = "A C-style string";
double d;
std::cin >> d;

2)成员函数初始化

注意不要搞混赋值和初始化

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++规定,对象的成员变量初始化要发生在进入构造函数本体之前。这样写带来的后果就是。这些成员的default构造函数会先对这些成员进行初始化,在初始化后又进行赋值。效率比较低,正确做法应该是使用member initialiazation list(成员初始列)来代替赋值。如下所示:

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){

}

3)不同编译单元内定义之non-local static 对象的初始化

如:

class FileSystem {
public:
	...
	std::size_t numDisks() const; //众多成员函数之一
	...
};
extern FileSystem tfs; //预备给客户使用的对象

class Directory { //客户程序类
public:
	Drectory(params);
	...
};
Directory::Directory(params){
	...
	std::size_t disks = tfs.numDisks() ; //使用tfs对象
	...
}

如果客户程序先一步初始化,则会调用未初始化的对象tfs,产生不可预知的后果。
而C++本身也确实无法设定不同编译单元内的non-local static 对象的初始化顺序。所以就要靠设计模式来解决了。
用Singleton模式比较巧妙的解决这一问题。

class FileSystem {
public:
	...
	std::size_t numDisks() const; //众多成员函数之一
	...
};
FileSystem& tfs(){
	static FileSystem fs;
	return fs;
}

class Directory { //客户程序类
public:
	Drectory(params);
	...
};
Directory::Directory(params){
	...
	std::size_t disks = tfs().numDisks() ; //使用tfs函数
	...
}

当然这样写不是线程安全的,线程安全的单例模式网上也有很多,这里只是举个例子。

总结

  • 为内置型对象进行手工初始化,因为C++不能保证初始化它们。
  • 构造函数最好使用成员初始列,而不要在构造函数本体内使用赋值操作。且初始列列出的成员变量,其排列次序应该与它们声明的次序一致。
  • 为免除“跨编译单元之初始化次序”问题,请以local static 对象替换non-local static 对象。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值