C++基础整理

面向过程

  • const:C++中的const和C中的const的作用有区别。在C中,const是个“冒牌货”,即可通过强制类型转换或指针间接修改const类型的变量;而在C++中,const类型的变量的值无论如何都不会被改变。
  • 顶层const:指针本身是个常量;底层const:指针所指的对象是个常量。因为引用一旦绑定到一个对象上就不能再更改了,所以引用本身都是顶层const,不必用const显式地声明。指向常量的引用或指针没有规定其所指的对象必须是一个常量,而仅仅要求不能通过该指针或引用改变对象的值,并没有规定那个对象的值不能通过其他途径改变。注意一种情况:
const int ci = 10; //ci有顶层const特性
auto e = &ci; //对常量对象取地址是一种底层const,即e是个指向常整型的指针
  • 使用范围for语句遍历数组时的注意点:
int ia[3][4] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
size_t cnt = 0;
for (auto &row : ia) {
    //此处的引用类型必不可少
	for (auto col : row) {
   
		cout << col << '\t';
	}
	cout << endl;
}
//如果row不是引用类型,编译器会把ia的每个本来是一维数组的各个元素
//当成指针类型赋给row,这样内层的循环就不合法了.
//因此,使用范围for语句处理多维数组时,除了最内层的循环外,
//其他所有循环的控制变量都应该是引用类型.
  • 对形如d1.d2d3的数,若要取出它的小数点后两位d2d3,应注意计算机表示可能是不精确的。例如,用计算机表示数值5.29美元时,可能要比5.29稍小。如用语句
double theAmounts = 5.29;
unsigned long dollars = (unsigned long)theAmounts;
unsigned int cents = (unsigned int)((theAmounts - dollars) * 100);

来抽取美分的值,那么用计算机来表示就会出错,因为(theAmounts - dollars) * 100要比29稍小。解决方法是给theAmounts加上0.001,这时只要d1.d2d3用计算机表示后与实际值相比不少于0.001或不多于0.009,结果就是正确的,即:

unsigned int cents = (unsigned int)((theAmounts + 0.001 - dollars) * 100);
  • 对代码的组织:
    • 通常,当调用一个函数时,编译器只需掌握函数的声明;当使用一个类类型的对象时,类定义必须是可用的,但成员函数的定义不必已经出现。因此,我们可将类定义和函数声明放在头文件中,而普通函数和类的成员函数的定义放在源文件中。
    • 上一点的做法不适用于模板,因为当我们使用(而不是定义)模板时,编译器才生成代码。为了生成一个实例化版本,编译器需掌握函数模板或类模板成员函数的定义。因此,模板的头文件通常既包括声明也包括定义。

面向对象

面向对象的基本特性

  1. 返回引用的函数是左值的,这意味着这些函数返回的是对象本身而非对象的副本。如:
inline Screen &
Screen::set(char c) {
   
	contents[cursor] = c;
	return *this;
}
  1. 友元:
    • 友元关系不存在传递性。如,类B是类A的友元类,类C是类B的友元类,并不能得出类C也是类A的友元类。
    • 友元声明的作用是影响访问权限。即使在一个类的内部定义了它的一个友元函数,也必须在类的外部提供该友元函数的声明,使得该友元函数可见。
  2. 多态何以可能:
    • 一个变量或表达式的静态类型是变量声明时或表达式生成时的类型,这在编译时可知;而一个变量或表达式的动态类型是变量或表达式表示的内存中的对象的类型,这直到运行时才可知。
    • 基类的指针或引用的静态类型可能与其动态类型不一致,即有可能存在派生类向基类的类型转换。这是因为每个派生类对象都包含一个基类部分,而基类的引用或指针可以绑定到该基类部分上。编译器只能通过检查指针或引用的静态类型来推断该转换是否合法。引用或指针的静态类型与动态类型不同这一事实正是C++支持多态的根本所在。
    • 注意与下述情况相区别:由于允许派生类向基类的转换,所以在出现基类指针或引用的地方都可以实际传递一个派生类对象。但如果这是在非虚函数中,则不会发生动态绑定。例如将一个派生类对象拷贝、移动或赋值给一个基类对象时,该操作只处理派生类对象的基类部分,而派生类中除基类部分之外的部分则被切掉了,即实际调用的仍是基类的拷贝构造函数、移动构造函数、拷贝赋值运算符。
  3. 使用structclass关键字定义的类之间的唯一差别是默认成员访问说明符和默认派生访问说明符:struct默认是public的,class默认是private的。不过,不建议依赖默认的设置,应显式声明。

拷贝控制

  1. 构造函数:
    • 对于类的数据成员,若没有在构造函数的初始值列表中显式地初始化成员,则该成员将在构造函数体之前执行默认初始化。若成员中有const成员或引用成员时,必须通过构造函数初始值列表为这些成员提供初始值,因为一个const变量或引用类型必须在定义时初始化。
  2. 拷贝初始化:
    • 依靠拷贝构造函数或移动构造函数来完成。
    • 何时发生拷贝初始化:
      • = 定义变量时;
      • 将一个对象作为实参传递给一个非引用类型的形参时;(值传递;这一点解释了为什么拷贝构造函数自己的参数必须是引用类型的:否则,这个拷贝初始化过程会陷入无限循环。)
      • 从一个返回类型为非引用类型的函数返回一个对象时;
      • 用花括号列表初始化一个数组中的元素或一个聚合类中的成员时。
    • 拷贝初始化的一点限制:若所使用的初始化值要求通过一个explicit的构造函数来进行类型转换,则必须显式使用该构造函数而不可隐式转换类型。如:
//假如vector<int> 的接受大小参数的构造函数是explicit的,那么:
vector<int> v1 = 10; //错误
vector<int> v2(10); //正确:直接初始化
void f(vector<int>); //f的参数进行拷贝初始化
f(10); //错误:不能用一个explicit的构造函数拷贝一个实参
f(vector<int>(10)); //正确:从一个int直接构造一个临时vector
  1. 析构函数:

    • 执行与构造函数相反的操作:释放对象使用的资源,并销毁对象的非static数据成员。
    • 由于它不接受参数,因此不可被重载。一个类只有唯一一个析构函数。
    • 何时调用析构函数:一个对象被销毁时:
      • 对象离开其作用域时;
      • 一个对象被销毁时,其成员也被销毁;
      • 容器被销毁时,其成员也被销毁;
      • 对于动态分配的对象,对指向它的指针执行delete运算符时,该对象被销毁;(注意:隐式销毁一个内置指针类型的成员不会delete它所指的对象;合成析构函数不会delete一个指针数据成员。)
      • 对于临时对象,当创建它的完整表达式结束时,它被销毁。
    • 在一个析构函数中,首先执行函数体,然后销毁成员,并且成员按初始化顺序(即按它们在类中出现的顺序)的逆序销毁。注意:析构函数体自身并不直接销毁成员,成员是在析构函数体之后隐含的析构阶段中被销毁的。
  2. 一个类是否需要自定义版本的拷贝控制成员的原则

    • 首先确定这个类是否需要一个析构函数,若是,则它也需要一个拷贝构造函数和一个拷贝赋值运算符;(有一个例外:基类通常需要定义一个虚析构函数,但不一定需要定义自己的拷贝构造函数和拷贝赋值运算符。)
    • 如果这个类需要一个拷贝构造函数,则它也需要一个拷贝赋值运算符。反之亦然。但不必然意味着它也需要析构函数。
  3. 如果一个类有数据成员不能默认构造、拷贝、复制或销毁,则对应的成员函数将被定义为删除的。(因此,一个类的析构函数不能是删除的,否则该类对象无法被销毁。而如果要阻止一个类的对象的拷贝,则需将它的拷贝构造函数和拷贝赋值运算符定义为删除的。)

  4. 拷贝赋值运算符通常执行拷贝构造函数和析构函数中也要做的工作。例如:

//定义一个类HasPtr,它的行为像一个值,即它有自己的状态;
//当拷贝一个该类对象时,副本和原对象是互相独立的;
//改变副本不会对原对象有任何影响,反之亦然。
class HasPtr {
   
	friend void swap(HasPtr &lhs, HasPtr &rhs);
	public:
		//constructor
		HasPtr(const string &s = string()) : 
				ps(new string(s)), i</
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值