1) 公有继承
纯虚函数 => 继承的是:接口 (interface)
普通虚函数 => 继承的是:接口 + 缺省实现 (default implementation)
非虚成员函数 =>继承的是:接口 + 强制实现 (mandatory implementation)
2) 不要重新定义一个继承自基类的非虚函数(never redefine an inherited non-virtual function)
3) override:可以显式的在派生类中声明,哪些成员函数需要被重写,如果没被重写,则编译器会报错。
const修饰成员函数
(1)该成员函数不能修改任何的成员变量(mutable修饰的变量除外)
(2)该成员函数不能调用非const成员函数,因为非const成员函数可能会修改成员变量
virtual
虚函数是在基类中使用关键字 virtual 声明的函数,虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的基类或子类对象,并自动调用类型的成员函数。
纯虚函数 :virtual int area() = 0;
通常通过基类指针只能访问派生类的成员变量,但是不能访问派生类的成员函数。有了虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员。换句话说,基类指针可以按照基类的方式来做事,也可以按照派生类的方式来做事,它有多种形态,或者说有多种表现方式,我们将这种现象称为多态。
#include
using namespace std;
//基类People
class People{
public:
People(char *name, int age);
void display();
protected:
char *m_name;
int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
cout<
}
//派生类Teacher
class Teacher: public People{
public:
Teacher(char *name, int age, int salary);
void display();
private:
int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
cout<
}
int main(){
People *p = new People("王志刚", 23);
p -> display();
p = new Teacher("赵宏佳", 45, 8200);
p -> display();
return 0;
}
王志刚今年23岁了,是个无业游民。
赵宏佳今年45岁了,是个无业游民。
我们直观上认为,如果指针指向了派生类对象,那么就应该使用派生类的成员变量和成员函数,这符合人们的思维习惯。但是本例的运行结果却告诉我们,当基类指针 p 指向派生类 Teacher 的对象时,虽然使用了 Teacher 的成员变量,但是却没有使用它的成员函数,导致输出结果不伦不类(赵宏佳本来是一名老师,输出结果却显示人家是个无业游民),不符合我们的预期。
换句话说,通过基类指针只能访问派生类的成员变量,但是不能访问派生类的成员函数。为了消除这种尴尬,让基类指针能够访问派生类的成员函数,C++ 增加了虚函数(Virtual Function)。使用虚函数非常简单,只需要在函数声明前面增加 virtual 关键字。
#include
using namespace std;
//基类People
class People{
public:
People(char *name, int age);
virtual void display(); //声明为虚函数
protected:
char *m_name;
int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
cout<
}
//派生类Teacher
class Teacher: public People{
public:
Teacher(char *name, int age, int salary);
virtual void display(); //声明为虚函数
private:
int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
cout<
}
int main(){
People *p = new People("王志刚", 23);
p -> display();
p = new Teacher("赵宏佳", 45, 8200);
p -> display();
return 0;
}
运行结果:
王志刚今年23岁了,是个无业游民。
赵宏佳今年45岁了,是一名教师,每月有8200元的收入。
int main(){
People p("王志刚", 23);
Teacher t("赵宏佳", 45, 8200);
People &rp = p;
People &rt = t;
rp.display();
rt.display();
return 0;
}
由于引用类似于常量,只能在定义的同时初始化,并且以后也要从一而终,不能再引用其他数据,所以本例中必须要定义两个引用变量,一个用来引用基类对象,一个用来引用派生类对象。从运行结果可以看出,当基类的引用指代基类对象时,调用的是基类的成员,而指代派生类对象时,调用的是派生类的成员。
不过引用不像指针灵活,指针可以随时改变指向,而引用只能指代固定的对象,在多态性方面缺乏表现力,所以以后我们再谈及多态时一般是说指针。本例的主要目的是让读者知道,除了指针,引用也可以实现多态。
多态使用:
#include
using namespace std;
class Troops{
public:
virtual void fight(){ cout<
};
//陆军
class Army: public Troops{
public:
void fight(){ cout<
};
class _99A: public Army{
public:
void fight(){ cout<
};
class WZ_10: public Army{
public:
void fight(){ cout<
};
class CJ_10: public Army{
public:
void fight(){ cout<
};
//空军
class AirForce: public Troops{
public:
void fight(){ cout<
};
class J_20: public AirForce{
public:
void fight(){ cout<
};
class CH_5: public AirForce{
public:
void fight(){ cout<
};
class H_6K: public AirForce{
public:
void fight(){ cout<
};
int main(){
Troops *p = new Troops;
p ->fight();
//陆军
p = new Army;
p ->fight();
p = new _99A;
p -> fight();
p = new WZ_10;
p -> fight();
p = new CJ_10;
p -> fight();
//空军
p = new AirForce;
p -> fight();
p = new J_20;
p -> fight();
p = new CH_5;
p -> fight();
p = new H_6K;
p -> fight();
return 0;
}
virtual type () const=0
成员函数后面用 const 修饰,通俗的理解就是在这个函数内不能修改类的成员变量,除非那个成员变量是 mutable 的
virtual void print() const = 0;
(1)=0说明它是纯虚函数,没有定义,只有接口,由子类继承实现
(2)const表明不能修改其数据成员
class aa{
int num;
public:
aa(){
int b =10;
num = b;
};
void out1(){
cout<
}
void out2() const{
cout<
}
void out3() const{
num+=10; //出错,const函数不能修改其数据成员
cout<
}
}
友元的正确使用能提高程序的运行效率,但同时也破坏了类的封装性和数据的隐藏性,导致程序可维护性变差。
友元函数 :
友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下:
friend 类型 函数名(形式参数);
友元类 :
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。
当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。定义友元类的语句格式如下:
friend class 类名;
其中:friend和class是关键字,类名必须是程序中的一个已定义过的类。
使用友元类时注意:
(1)友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
inline 避免了频繁调用函数对栈内存重复开辟所带来的消耗,比如递归函数,inline仅是一个对编译器的建议 inline的使用是有所限制的,inline只适合函数体内代码简单的函数使用,不能包含复杂的结构控制语句例如while、switch,并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)。
c++11 类默认函数的控制:"=default" 和 "=delete"函数
C++ 的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。
这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。
如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。
C++11 标准引入了一个新特性:"=default"函数。程序员只需在函数声明后加上“=default;”,编译器将为显式声明的 "=default"函数自动生成函数体。
"=default"函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数。
"=default"函数既可以在类体里(inline)定义,也可以在类体外(out-of-line)定义。
class X
{
public:
X() = default; //该函数比用户自己定义的默认构造函数获得更高的代码效率
}
// 为了能够让程序员显式的禁用某个函数,C++11 标准引入了一个新特性:"=delete"函数。程序员只需在函数声明后上“=delete;”,就可将该函数禁用。
class X3
{
public:
X3();
X3(const X3&) = delete; // 声明拷贝构造函数为 deleted 函数
X3& operator = (const X3 &) = delete; // 声明拷贝赋值操作符为 deleted 函数
};
// "=delete"函数特性还可用于禁用类的某些转换构造函数,从而避免不期望的类型转换
class X4
{
public:
X4(double)
{
}
X4(int) = delete;
};
// "=delete"函数特性还可以用来禁用某些用户自定义的类的 new 操作符,从而避免在自由存储区创建类的对象
class X5
{
public:
void *operator new(size_t) = delete;
void *operator new[](size_t) = delete;
};