1、友元
(1)友元函数
一个类的友元函数可以访问该类的私有成员。
class me;//提前声明me类,以便后面friend类使用,把me类写前面可省
class friend
{
public:
void yourscore(me* p)
{cout<<p->score;};友元函数可以访问私有成员
};
class me
{
friend void yourscore(me* p);//友元函数声明
friend void func(me* p);
private:
int score;
};
void func(me* p)
{p.score = 100;};
(2)友元类
如果A是B的友元类,那么A的成员函数可以访问B的私有成员。
class me
{
friend class friend;//友元类声明
private:
int score;
};
class friend
{
public:
me x;
void yourscore()
{cout<<x.score;};友元类可以访问私有成员
};
2、继承
在定义一个新的类B时,如果类B拥有类A的全部特点,那么可以把类A作为一个基类,把类B作为一个派生类。派生类就是对基类经过修改和扩充得到。派生类拥有基类的全部成员函数(变量),但是派生类不能访问基类的私有成员。
class 派生类名: public 基类名{...};
class Student
{
private:
string sName;
int sScore;
public:
void SetName(const string& name)
{
sName = name;
}
void SetScore(const int& score)
{
sScore = score;
}
};
//派生类
class Graduate:public Student//由Student类派生而来
{
private:
string supervisor;//派生类可以对基类扩充
public:
int s = 100;
void SetScore()
{
Student::SetScore(s);
Student::sScore = 20;//错
};//派生类可以对基类成员覆盖
};
派生类构造函数
class Student
{
private:
string sName;
int sScore;
public:
Student(string name,int score):sName(name),sScore(score){}
};
//派生类
class Graduate:public Student
private:
string supervisor;
public:
Graduate(string name,int score,string s);
};
Graduate::Graduate(string name,int score,string s):Student(name,score)//派生类构造函数
{
supervisor=s;
}
public继承兼容赋值规则
3、多态
通过基类指针或引用调用一个同名虚函数时,编译器不确定到底调用的是基类还是派生类的函数,运行时才确定——动态联编。
(1)虚函数
在类的定义中,前面有virtual关键字的成员函数就是虚函数。virtual关键字只用写在类定义里的函数声明中,写函数体时不用。构造函数和静态成员函数不能是虚函数。
class base
{
virtual int get();
};
int base::get()
{}
(2)多态的表现形式
(i)根据public继承兼容赋值规则,派生类的指针可以赋给基类指针。通过基类指针调用基类或派生类中的同名虚函数时:(1)若指针指向一个基类对象,那么被调用的就是基类的虚函数;(2)若指针指向一个派生类对象,那么被调用的就是派生类的虚函数;这种机制叫“多态”。
class base
{
public:
virtual void func();
};
class derived:public base
{
public:
virtual void func();//同名虚函数func
};
void base::func(){}
void derived::func(){}
int main()
{
base b1;
derived d;
base* p1 = &b1;//基类指针指向基类对象,调用基类虚函数
base* p2 = &d;//基类指针指向派生类对象,调用派生类虚函数
p1->func();//调用基类虚函数
p2->func();//调用派生类虚函数
}
(ii)根据public继承兼容赋值规则,派生类的对象可以赋给基类引用。通过基类引用调用基类或派生类中的同名虚函数时:(1)若引用指向一个基类对象,那么被调用的就是基类的虚函数;(2)若引用指向一个派生类对象,那么被调用的就是派生类的虚函数;这种机制叫“多态”。
class base
{
public:
virtual void func();
};
class derived:public base
{
public:
virtual void func();//同名虚函数func
};
void base::func(){}
void derived::func(){}
int main()
{
base b1;
derived d;
base& r1 = b1;//基类的引用引用基类对象,调用基类虚函数
base& r2 = d;//基类的引用引用派生类对象,调用派生类虚函数
r1.func();//调用基类虚函数
r2.func();//调用派生类虚函数
}
(iii)在非构造函数和非析构函数中,成员函数中调用虚函数是多态。在构造函数和析构函数中调用虚函数不是多态。
class base
{
public:
void func1(){func2();}//相当于this->func2(),基类this指针指向虚函数
virtual void func2();
};
class derived:public base
{
public:
virtual void func2();//同名虚函数func2
};
void base::func2(){}
void derived::func2(){}
int main()
{
derived d;
base* p = &d;
p->func1();//p指向派生类对象,相当于this指向派生类对象,所以调用派生类虚函数。
}
多态有利于程序的扩充。一种常见多态操作就是用基类指针数组存放指向各种派生类对象的指针,然后遍历数组,就能对各个派生类对象进行操作。
(3)虚析构函数
通过基类指针删除派生类对象时,通常情况下只调用基类的析构函数。解决办法就是对基类的析构函数声明为virtual,派生类的析构函数可以不加virtual。这样的话,通过基类指针删除派生类对象时,会先调用派生类的析构函数,再调用基类的析构函数。一般,一个类如果定义了虚函数或打算作为基类使用,应该把析构函数写成虚函数。
(4)纯虚函数和抽象类
没有函数体的虚函数。可以在成员函数中调用纯虚函数(多态),但在构造和析构函数中不能。
class base
{
public:
virtual void func() = 0;//虚函数,没有函数体,不用再写定义了
};
包含纯虚函数的类叫抽象类。
抽象类:(1)抽象类只能作为基类;
(2)不能创建抽象类的对象;A a;//错误,A是抽象类
(3)可以定义抽象类的指针或引用;A* p;A& r;//正确
(4)抽象类的指针或引用可以指向由抽象类派生出来的类的对象。由抽象类派生出来的类是指这个类实现了基类(抽象类)中所有的纯虚函数。
class base
{
public:
virtual void func() = 0;
void g(){this->func();}//调用的是派生类的func
};
class derived:public base
{
public:
void func();
};
void derived::func(){cout << "aaa";}//派生类中一定要实现抽象类的纯虚函数
int main()
{
derived d;
d.g();//多态
}