c++笔记(三)

一、只有在绝对确认以后源对象没有其他用户时,才能使用移动操作,因为移动操作的移后源对象都具有不确定的状态。

二、引用限定符限定this指针所指对象的左值、右值属性,如有const限定符,需跟在其后,必须同时出现在声明和定义位置。如果一个成员函数有引用限定符,则具有同参数列表的所有版本都要有引用限定符。

三、重载运算符要么是类的成员,要么有至少一个类类型参数,不能改变作用于内置类型的运算符的含义。

四、每个类控制自己成员的初始化过程。派生类先调用基类的构造函数初始化派生类中的基类部分,再按照声明的次序初始化派生类的成员。

五、(c++11) final关键字防止继承发生。

六、即使一个基类的指针或引用已经绑定到一个派生类对象上了,我们也不能执行基类向派生类的转换,因为编译器只能通过检查指针或引用的静态类型来确定转换是否合法。解决方法见:https://blog.csdn.net/qq_38216239/article/details/80489532

class B
{
  //..
};
class A : public B
{
  //..
};
A a;
B* pb = &a; //pb绑定到子类A的对象
A* pa = pb; //错误,试图执行父类向子类的转换

七、 用一个子类对象给父类对象赋值或初始化时,调用父类对象的拷贝构造函数或构造函数,它们都接受一个const父类类型的引用参数,所以将子类对象绑定到引用上是合法的,但是,上述函数只会处理子类对象中的父类部分,子类自己独有的部分将被切掉(sliced down)。

八、子类覆写父类继承来的虚函数时,形参应完全一致,返回类型通常也应该一致,但是当返回类型是指向类自身的指针或引用时,允许不同,但是这时从子类到父类的转换要求是可访问的:

class B
{
public:
  //..
  virtual B* f(int i) { /*..*/ return pb;}
  //..
private:
  B* pb;
};
class A : public B
{
public:
  //..
  virtual A* f(int i) { /*..*/ return pa;}
  //..
private:
  A* pa;
};

九、虚函数的默认实参使用的是基类中的默认实参,如果真的需要默认实参,最好基类和派生类中的默认实参保持一致。

十、通常派生类的虚函数函数体中需要调用它的基类版本来完成某些共通的任务,这时要用作用域运算符来回避虚函数机制。

十一、纯虚函数可以没有定义,含有纯虚函数的类是抽象基类,它负责定义接口,子类来各自实现这些接口。不能创建抽象基类的对象。

扫描二维码关注公众号,回复: 2012323 查看本文章

十二、子类的成员或友元只能通过子类的对象来访问父类的受保护成员,而不能直接访问父类的受保护成员。

class F
{
protected:
  int _pro = 0;
};
class S : public F
{  
friend void f(S& s);
friend void f(F& f);
};
F f;
S s;
void f(S& s) { cout << s._pro << endl;} //ok
void f(F& f) { cout << f._pro << endl;} //error,只能通过S的对象访问_pro成员  

十三、 父类的访问说明符控制子类(及其友元)对父类成员的访问权限,派生访问说明符控制子类用户(包括子类的子类在内)对原父类成员的访问权限。

十四、名字查找是在编译时进行的,因此,一个对象、引用或指针的静态类型决定了哪些成员是可见的。

class F {};
class S : public F
{public: 
  void f() {} };
S s;
F* pf = &s;
S* ps = &s;
pf->f();//error,f()对pf不可见
ps->f();//ok

十五、被用作父类的类的析构函数要定义为虚函数,这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。子类的拷贝构造和移动构造函数应该在初始值列表中显式的使用父类的拷贝构造或移动构造函数,子类的赋值运算符也要先调用父类的赋值运算符为父类部分赋值,而子类的析构函数只需要销毁自己的资源就行。

十六、子类对象的构造和析构函数执行的过程中,对象处于未完成状态,如果构造时子类部分尚未完成,析构时子类部分已被销毁,则在构造函数和析构函数中调用虚函数都不会发生动态绑定,而是调用父类的版本。应该避免在构造函数和析构函数中调用虚函数。

十七、希望在容器中存放具有继承关系的对象时,实际存放的是指向父类对象的(智能)指针,这样不管是子类对象还是父类对象都能存入其中,且不会发生子类对象被切掉部分的现象。

vector<shared_ptr<F>> V;
V.push_back(make_shared<F>(/*..*/));
V.push_back(make_shared<S>(/*..*/));//子类指针转换为父类指针

十八、泛型编程的两个重要原则:(减少对实参类型的要求)

  • 模板中的函数参数是const引用类型:保证了不能用于拷贝的类型也能使用这些模板。
  • 函数体中的判断条件仅使用小于号。

十九、模板类的函数成员只有在使用到时才会实例化,没有使用时不会实例化,所以即使某种类型不能完全符合模板操作的要求,仍能使用该类型来实例化。

二十、类模板的static数据成员必须定义成模板:

template<typename T>
class A
{
  //..
private:
  static int i;
};
template<typename T> int A<T>::i = 1;//定义并初始化i

每个不同实例化的类对应不同的static对象,同一实例化的类的不同对象共享一个static成员。

猜你喜欢

转载自blog.csdn.net/qq_38216239/article/details/80711807