4.2.4指针的使用
指针+整数其实是地址+sizeof(type),其实就是下一个对象的首地址。而不是简单的加上整数个字节。
普遍的const int *p;指向const对象,不能通过指针修改对象,但可以修改指针指向其他对象。
少见的 int *const p=&a;const常量指针,指针地址不能修改,必须初始化(跟普通const变量性质一样)。但可以通过指针修改对象内容。
区分这两种指针主要看const在*前还是后。正常的是在*左面。
7.8重载函数
只有在同一作用于声明的函数才会构成重载。如果在一个作用于里面声明一个函数名,将会屏蔽外层的同名函数。 (声明一个与外层函数名相同的变量也会有屏蔽作用)
重载++时,operator++(int)使用int来代表重载后递增a++,operator++()表示++a
如父类的构造函数被重写为带有才参数时,子类的成员初始化列表必须显式调用父类构造函数,否则编译出错。
//Base的构造函数是Base(int m); class Derived:public Base { Derived():Base(3){} };
自增符号重载:区分前置后置
class A { public: int val; void operator++(){//没有int参数,标志此函数为前置函数 val++; } A operator++(int){//必须返回值而非返回引用,因为tmp是局部临时变量 A tmp=(*this);//int参数是用于标记此函数是后置函数 val+=1; return tmp; } }; void operator++(A& a,int){}//如非成员函数,则第二个参数是int代表是后置符号
12.1类的声明与定义
1、使用初始化列表和使用构造函数体初始化的区别。
一、const成员只能被初始化不能被赋值,因此一定要用初始化列表。二、当有带有参数初始化的类作为本类的成员时,因为缺乏默认构造函数,作为本类的成员缺不能初始化,因此也必须使用初始化列表。
而且,使用初始化列表在特定情况会效率更高(否则会先调用一次默认构造函数,再进行一次赋值)。
友元可以访问私有成员。可定义友元类和友元函数。派生类用户代码不可以访问基类的私有成员,但可以访问派生类对象的保护成员。
派生类用户代码可以访问自己这个派生类的其他对象的保护成员,但不可以访问基类对象的保护成员。P476
总结:子类之间的protect相当于public,子类遇到父类的protect就相当于遇见private(不同的是连子类内部都不可以访问从父类继承下来的私有成员)
class classfather { private: int pri; protect: int pro; }; void classson::fun(classson &son,classfather &father) { int k=pro;//OK 访问自己的保护成员 k=son.pro;//OK 访问其他子类对象的保护成员 k=father.pro;//ERROR 类的外部,不能访问保护成员。 k=this->pri;//ERRO 不可以访问自己的父类私有成员 }
15.1面向对象的概述
通过定义基类的成员函数为虚函数,表示启用动态绑定。使用基类的引用或者指针作为函数的参数。实参可使用基类或派生类。实参的类型决定调用基类虚函数还是派生类的重定义虚函数。这就是多态(动态绑定)。多态是通过基类的引用或者指针来实现的。
static成员函数不能定义为虚函数(需要的时候直接定义为子类的静态函数即可)。
static标识符作用:1、隐藏-全局变量、函数其他文件可见,加上static以后本文件可见。2、保持变量内容持久3、static变量(全局区的都会)一定会初始化为0。
对于使用父类的指针或者引用调用非虚函数,不管子类有没自定义的函数版本,编译时已经确定调用父类版本函数。
如传入了子类引用,但不需要使用动态绑定,可用父类作用于classbase::func()来强制使用父类虚函数调用。
15.2.5三种继承方式
公有继承:子类保持父类的访问标签,父类为public的子类也为public,父类protect子类也是protect
保护继承:父类的public和protect都变成子类的protect
私有继承:无论父类的标签如何,子类全部变成private
public | protected | private | |
公有继承 | public | protected | 不可见 |
私有继承 | private | private | 不可见 |
保护继承 | protected | protected | 不可见 |
概念:
派生类代码指的是实现派生类的代码。派生类用户是指使用派生类对象的人,这些人不能改变派生类代码,只能使用派生类。
无论哪一种继承方式,派生类代码对成员的访问权限都一样,都根据父类标签决定。
继承方式决定了派生类用户对成员的访问权限。
如:class son:private father{int k=i;//i是父类的保护成员}//OK,虽然继承下来i是private,但可以访问自己的私有成员。
son s; int k=s.i;//ERROR,派生类用户不可以使用私有成员。
友元不能继承。如果派生类需要设置友元,则需自身重新设置,并不会继承父类的友元设置。
整个继承层次中,static成员只会有一个实例。也就是说父类和派生类共用static实例。
派生类的构造哈数可以包含基类在初始化列表中初始化基类成员,但不可以直接初始化基类成员,如
son():a(1),b(2),father(3,3){}//父类father含有两个成员,初始化为3 3
通过在函数参数列表后加=0说明次函数是纯虚函数。纯虚函数的作用是作为接口供子类继承,但在本版本中不能使用。virtual void func()=0;
只有重载了纯虚函数的版本才能产生实例,否则会编译错误。含有纯虚函数的类为抽象类。
为何要使用初始化成员列表:
调用类的构造函数分为两个过程:1、初始化过程 2、执行构造函数过程。初始化过程先执行。如有初始化成员列表,则按成员列表初始化,否则为各成员调用默认构造函数进行初始化。完成初始化阶段后,再执行构造函数。
因此,如在执行构造函数时才对成员进行赋值初始化,则会导致各成员先调用一次默认构造函数,再在执行阶段使用赋值函数,浪费效率。因此,能使用初始化列表的尽量使用。
class test { int a; public: test(){ cout<<"default constructor"<<endl; } test& operator=(test &t){ this->a=t.a; cout<<"= operator"<<endl; return *this; } test(test &t){ this->a=t.a; cout<<"copy constructor"<<endl; } }; class test2 { public: test a; test2(test &t){ a=t; } }; int main() { test a; test b=a;//初始化,就算使用=,也是调用copy constructor test c(a); a=b; cout<<endl; test2 e(a);//先输出defaut constructor,再输出= operator return 0; }