1、继承
1.1、概述
继承与派生是同一过程从不同的角度看 – 保持已有类的特性而构造新类的过程称为继承(inherit) – 在已有类的基础上新增自己的特性而产生新类的过程称为派生(derive) 。 – 子类继承了父类,父类派生了子类
#include <iostream>
using namespace std;
#define PI 3.14159
class Point // 定义"点"类
{
int x, y;
public:
Point(int a=0, int b=0)
{ x=a; y=b; }
void ShowPoint( )
{ cout<<"Point:("<<x<<','<<y<<")\n"; }
int Getx( )
{ return x; }
int Gety( )
{ return y; }
void Setxy(int a, int b)
{ x=a; y=b; }
};
class Circle: public Point // 定义"圆"类,公有继承
{
int r; // "圆"的半径
public:
Circle(int x, int y, int ra) : Point(x, y) // B
{ r = ra; }
void Setr(int ra)
{ r = ra; }
double Area( ) //求圆的面积
{ return PI*r*r; }
void Move(int x_offset, int y_offset) //将圆心坐标平移
{
int x1=Getx( ); //存取基类的私有成员
int y1=Gety( ); // D
x1 += x_offset; y1 += y_offset;
Setxy(x1, y1); // E
}
void ShowCircle( )
{
ShowPoint( ); // F
cout<<" Radius: "<<r<<'\t';
cout<<"Area: "<<Area( )<<endl; //G
}
};
int main()
{
Circle c(0, 0, 2); // 0,0表示圆心坐标的初值,
//2表示半径的初值
c.ShowCircle();
c.Move(2, 2);
c.ShowCircle();
c.Setxy(0, 0); // 重新设置圆心坐标
c.Setr(1); // 重新置半径值
c.ShowCircle();
return 0;
}
C++允许派生类 – 继承或修改基类的部分或全部属性和行为, – 增加基类中没有的新的属性和行为
1.2、继承的种类
C++中, 一个派生类可以从一个基类派生,也可以从多个基类派生。 从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承
1.3、继承的方式
?1.3.1、公有继承
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
void get_value() //输入基类数据成员函数
{
cout << "Input num name sex:";
cin >> num >> name >> sex;
}
void display()
{
cout << "num:" << num << endl;
cout << "name:" << name << endl;
cout << "sex:" << sex << endl;
}
private: //基类私有成员
int num;
string name;
char sex;
};
class Student1 :public Student //公有继承
{
public:
void get_Value1()
{
cout << "Input age addr:";
cin >> age >> addr;
}
void display_1()
{
cout << "age:" << age << endl;
cout << "addr:" << addr << endl; //访问派生类私有成员,正确
}
private:
int age;
string addr;
};
int main()
{
Student1 stud; //定义派生类对象stud
//stud.get_value(); //访问基类公有成员函数
stud.get_Value1(); //访问派生类公有成员函数
stud.display(); //访问基类公有成员函数
stud.display_1();
return 0;
}
1.3.2、私有继承
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
void get_value() //输入基类数据成员函数
{
cout << "Input num name sex:";
cin >> num >> name >> sex;
}
void display()
{
cout << "num:" << num << endl;
cout << "name:" << name << endl;
cout << "sex:" << sex << endl;
}
private: //基类私有成员
int num;
string name;
char sex;
};
class Student1 :private Student
{
public:
void get_value1()
{
get_value(); //基类的公有成员在派生类变为私有的,在派生类内可以随便访问
cout << "Input age addr:";
cin >> age >> addr;
}
void display_1()
{
display();
cout << "age:" << age << endl;
cout << "addr:" << addr << endl; //访问派生类私有成员
}
private:
int age;
string addr;
};
int main()
{
Student1 stud1; //定义派生类对象stud1
stud1.get_value1(); //访问派生类公有成员函数
stud1.display_1();
//stud1.get_value();
return 0;
}
?1.3.3、保护继承( projected)
#include <iostream>
#include <string>
using namespace std;
class Student
{
//public:
//protected: //基类保护成员
int num;
string name;
char sex;
};
class Student1 :protected Student //保护继承
{
public:
void get_value1(); //派生类公有成员函数
void display1();
private:
int age; //派生类私有数据成员
string addr;
};
void Student1::get_value1()
{
cout << "Input num name sex:";
cin >> num >> name >> sex;
cout << "Input age addr:";
cin >> age >> addr;
}
void Student1::display1()
{
cout << "num:" << num << endl; //访问基类的保护成员,正确
cout << "name:" << name << endl;
cout << "sex:" << sex << endl;
cout << "age:" << age << endl; //访问派生类私有成员,正确
cout << "addr:" << addr << endl;
}
int main()
{
Student1 stud1; //定义派生类对象stud1
stud1.get_value1();
stud1.display1();
return 0;
}
?1.4、单继承
基类的构造函数和析构函数是不能被继承,需要在派生类中重新定义。
由于派生类继承了基类的成员,在初始化时,也要同时初始化基类成员。可通过调用基类的构造函数完成初始化
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student(int n, string nam, char s) //定义基类构造函数
{
num = n;
name = nam;
sex = s;
}
~Student(){} //基类析构函数
protected: //基类保护成员
int num;
string name;
char sex;
};
class Student1 :public Student //公有继承
{
public:
Student1(int a, string ad, char s, int n, string nam) :Student(n, nam, s)
{
age = a;
addr = ad;
}
void show()
{
cout << "num:" << num << endl; //访问基类的保护成员,正确
cout << "name:" << name << endl;
cout << "sex:" << sex << endl;
cout << "age:" << age << endl; //访问派生类私有成员,正确
cout << "addr:" << addr << endl;
}
~Student1(){} //派生类析构函数
private:
int age; //派生类私有数据成员
string addr;
};
int main()
{
Student1 stud1(19, "Wang_liBeijing;", 'f', 10010, "Wang_li");
//定义派生类对象stud1
Student1 stud2(21, "Shanghai", 'm', 10011, "Zhang_fang");
stud1.show();
stud2.show();
return 0;
}
1.4.1、多级派生时的构造函数
前面已介绍过多级派生的概念。即一个类不仅可以派生出一个派生类,这个派生类 还可以继续派生,形成派生的层次结构。 不需要列出每一层派生类的构造函数,只需写出其直接基类的构造函数即可。
#include <iostream>
#include <string>
using namespace std;
class Student //基类
{
public:
Student(int n, string nam)
{
num = n;
name = nam;
}
void display()
{
cout << "num:" << num << endl;
cout << "name:" << name << endl;
}
protected:
int num;
string name;
};
class Student1 :public Student //派生类1级
{
public:
Student1(int n, string nam, int a) :Student(n, nam)
//派生类构造函数
{
age = a; //此处只对派生类新增的数据成员初始化
}
void show()
{
display();
cout << "age:" << age << endl;
}
private:
int age;
};
class Student2 :public Student1 //派生类2级
{
public:
Student2(int n, string nam, int a, int s) :Student1(n, nam, a)
{
score = s;
}
void show_all()
{
show();
cout << "score:" << score << endl;
}
private:
int score;
};
int main()
{
Student2 stud(10010, "Li", 17, 89);
stud.show_all();
return 0;
}
1.5、多继承
多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个继承。
?有两个基类i、 j, 而k是i, j的派生类,在派生时, 对于i类为公有派生,对于j类为私有 派生。 –这样对于基类成员在派生类中可访问性不同,处理方式也不同。派生类对象不能直接访问基类的私有变量;需要在类通过私有基类的成员函数对其访问赋值。
#include <iostream>
using namespace std;
class i{ //基类
int a;
public:
void seta(int x){
a = x;
}
void showa(){
cout << "a=" << a << endl;
}
};
class j{ //基类
int b;
public:
void setb(int x){
b = x;
}
void showb(){
cout << "b=" << b << endl;
}
};
class k :public i, j { //定义k类,对i类为公有派生,对j类为私有派生
int c;
public:
void setc(int x, int y)
{
c = x;
setb(y);
//对j类的私有成员,k类无权访问,因此调用setb()函数对j类中的b赋值
}
void showc()
{
showa();
showb();
cout << "c=" << c << endl;
}
};
int main()
{
k obj; //定义k类对象obj
obj.seta(5);
obj.setc(7, 9);
obj.showc();
obj.showa(); //调用i类中的成员函数
}
1.5.1、 多继承的构造函数和析构函数
1.5.2、在派生类中引用公共基类中成员时出现二义性
在继承与派生的类层次结构中,被继承的多个基类如果有一个共同的基类,在派生类中访问这个共同基类的成员时也会产生二义性问题。
解决这种二义性方法也有两种: (1)使用作用域标识符 (2)虚基类
#include<string>
#include<iostream>
using namespace std;
class L1 //基类
{
public:
int m1;
void f1(){cout<<"layer 1-> m1="<<m1<<endl;}
};
class L2_1:public L1 //一级派生类1
{
public:
int m2_1;
};
class L2_2:public L1 //一级派生类2
{
public:
int m2_2;
};
class L3:public L2_1,public L2_2 //2级派生类
{
public:
int m3;
void f3(){cout<<"layer 3-> m3="<<m3<<endl;}
};
int main()
{
L3 obj;
obj.m3=4;
obj.f3();
obj.L2_1::m1=5; //正确!使用直接基类
obj.L2_1::f1(); //正确!使用直接基类
obj.L2_2::m1=6; //正确!使用直接基类
obj.f1(); //正确!使用直接基类
return 0;
}
1.6、虚基类
?
#include <iostream>
using namespace std;
class base
{
public:
base(){ cout << "this is base class!" << endl; }
};
class base2
{
public:
base2(){ cout << "this is base2 class!" << endl; }
};
class level1 :public base2, virtual public base
{
public:
level1(){ cout << "this is level1 class!" << endl; }
};
class level2 : public base2, virtual public base
{
public:
level2(){ cout << "this is level2 class!" << endl; }
};
class toplevel :public level1, virtual public level2
{
public:
toplevel(){ cout << "this is toplevel class!" << endl; }
};
int main()
{
toplevel topobj;
1.7、 基类和派生类的转换
#include <iostream>
using namespace std;
class Point
{
protected:
int x, y; // 保护成员
public:
Point(int a = 0, int b = 0) { x = a; y = b; }
void Show()
{
cout << "point(" << x << ',' << y << ")\n";
}
};
class Line : public Point // 公有继承
{
protected:
int x1, y1;
public:
Line(int a, int b, int c, int d) : Point(a, b) //调用基类构造函数
{
x1 = c; y1 = d;
}
void Show()
{
cout<< "线段端点1:";
Point::Show();
cout << "线段端点2:Point(" << x1 << ',' << y1 << ")\n";
}
};
int main()
{
Line line(2, 2, 6, 6);
line.Show();
Point p;
p = line; // A 赋值兼容规则
p.Show();
return 0;
}
1.8、LIO-SAM
class FeatureExtraction : public ParamServer
class ImageProjection : public ParamServer
class IMUPreintegration : public ParamServer
class mapOptimization : public ParamServer
//ParamServer类中的部分参数
class ParamServer
{
public:
ros::NodeHandle nh;
std::string robot_id;
}
在上面的类继承了ParamServer后可直接使用他内部的公有成员
如:
subImu = nh.subscribe<sensor_msgs::Imu>(imuTopic, 2000, &ImageProjection::imuHandler, this, ros::TransportHints().tcpNoDelay());
其中nh和imuTopic都是ParamServer内部的公有成员
2、多态
2.1、概述
?
?2.2、运算符重载
运算符重载是指对已有的运算符赋予它新的含义
运算符重载是C++实现静态多态的一种重要手段?
?两种重载方式: – 重载为类的成员函数(非静态的) – 重载为类的友元函数
?2.2.1、用成员函数重载运算符
#include <iostream>
using namespace std;
class A
{ int i;
public:
A(int a=0){ i=a; }
void Show(){ cout<<"i="<<i<<endl; }
void AddA(A &a, A &b) //利用函数进行类之间的运算
{ i=a.i+b.i; }
A operator +(A &a) //重载运算符+
{ A t;
t.i=i+a.i; //等价于this.i+a.i
return t;
}
};
int main()
{ A a1(10),a2(20),a3;
a1.Show ();
a2.Show ();
cout<<endl;
a3=a1+a2; //a3= a1.operator+(a2)
a3.Show();
cout<<endl;
a3.AddA(a1,a2); //调用专门的功能函数
a3.Show ();
return 0;
}
?
2.2.2、用友元函数重载运算符
如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的友元 – 友元函数是在类外的普通函数,与一般函数的区别是可以调用类中的私有或保护数据。 ? 将运算符的重载函数定义为友元函数, 参与运算的对象全部成为函数参数
#include <iostream>
using namespace std;
class Complex //复数类
{ public:
Complex() {real=0;imag=0;}
Complex(double r, double i) {real=r; imag=i; }
friend Complex operator+(Complex &c1, Complex &c2); //复数加重载函数声明,友元函数
void display();
private:
double real;
double imag;
};
Complex operator+(Complex &c1, Complex &c2) //复数加重载函数实现
{return Complex(c1.real+c2.real, c1.imag+c2.imag);}
void Complex::display()
{cout<<"("<<real<<","<<imag<<"i)"<<endl;}
int main()
{ Complex c1(3,4), c2(5,-10),c3;
c3=c1+c2;//c3=operator+( c1, c2)
cout<<"c1="; c1.display();
cout<<"c2="; c2.display();
cout<<"c1+c2="; c3.display();
return 0;
}
2.2.3、几个特殊的运算符重载
1、单目运算符(++ , --)
普通成员函数
#include <iostream>
using namespace std;
class Complex {
private:
double real, image;
public:
Complex(double x = 0, double y = 0){
real = x; image = y;
}
Complex(Complex &c){
real = c.real; image = c.image;
}
void Show(){
cout << real << " + " << image << "i" << endl;
}
Complex & operator ++() // 实现前置"++"运算符的函数成员
{
real++; image++; // 运算规则:实部与虚部各加1
return *this; // 返回前置"++"表达式的结果:加1之后对象的引用
}
Complex operator ++(int) // 实现后置"++"运算符的函数成员
{ //注意形参表中的整型参数
Complex temp=*this;
real++; image++; // 运算规则:实部与虚部各加1
return temp; // 返回后置"++"表达式的结果:加1之前的对象 }
}
};
int main()
{
//前置自增运算
Complex c1(1, 3), c2;
c2 = ++c1; // 前置自增运算 c2=operator ++(c1) return一个++后的this对象
c1.Show( ); // 显示复数c1,显示结果:2+4i,实部与虚部各加1
c2.Show( ); // 显示复数c2,显示结果:2+4i,即c1自增之后的结果
cout<<endl;
//后置自增运算
Complex c3(1, 3), c4;
c4 = c3++; // 后置自增运算
c3.Show( ); // 显示复数c3,显示结果:2+4i,实部与虚部各加1
c4.Show( ); // 显示复数c4,显示结果:1+3i,即c1自增之前的结果
}
友元
#include<iostream>
using namespace std;
class Point
{
public:
Point(int xx=0,int yy=0){x=xx; y=yy;}
void display(){cout<<"("<<x<<","<<y<<")"<<endl;}
friend Point operator++(Point &p); //声明重载函数为友元函数
friend Point operator++(Point &p,int); //声明重载函数为友元函数
private:
int x,y;
};
Point operator++(Point &p) //前置式++p1调用
{
return Point(++p.x,++p.y);
}
Point operator++(Point &p,int) //后置式p1++调用
{
return Point(p.x++,p.y++);
}
int main()
{
Point p1(1,2),p2;
p2=++p1; //前置式,先自增再赋值
p1.display();
p2.display();
cout<<endl;
p2=p1++; //后置式,先赋值再自增
p1.display();
p2.display();
return 0;
}
2、双目运算符
一般情况下,单目运算符常重载为类的成员函数, 双目运算符常重载为类的友元函数
3、特殊运算符重载
在C++中使用流提取运算符“<<”和流插入运算符“>>”执行输入、输出操作
cin和cout分别是istream类和ostream类的对象。 ? 用户自己定义的类型的数据,是不能直接用“ <<”和“>>”来输出和输入的。 ? 如果想用它们输出和输入自己定义的类型的数据,必须在自己定义的类中对这两个运算符进行重载
#include<iostream>
using namespace std;
class Complex //声明Complex类
{
public:
Complex(double r=0,double i=0)
{
real = r ;
imag = i ;
}
friend ostream & operator<<(ostream &output,Complex &c);
//运算符重载为友元函数
private:
double real,imag;
};
ostream & operator<<(ostream &output,Complex &c)
//流插入运算符重载函数的定义, 第一个参数为output
{
output<<'('<<c.real<<'+'<<c.imag<<"i)"<<endl;
return output;
}
int main()
{
Complex c1(3,4),c2(10,23);
cout<<c1; // 调用流插入运算符重载函数
cout<<c1<<c2; //c1和c2都为Complex类的对象
return 0;
}
?
2.3、虚函数
?
?
?在继承一章讲虚函数是为了节省空间
?为了实现派生类对象调用不同派生层次的同名函数,C++引入虚函数的概念。 – 通过在基类中将同名的成员函数设为虚函数,当用基类的指针或引用指向派生类时,实际运行时调用的是实际指向或引用的对象的相应函数而不是基类的同名函数
在派生类中可以重新定义与基类同名的函数, 必须通过指向基类的指针或基类对象的引用来调用虚函数, 确定是调用基类同名函数,或者派生类同名函数,才能实现动态多态性
#include <iostream>
using namespace std;
class A
{
public:
A( ) { fun( ); }
virtual void fun( )
{ cout << "A::fun" << '\t'; }
};
class B: public A
{
public:
B( ) { fun( ); }
void fun( )
{ cout << "B::fun" << '\t'; }
void g( )
{ fun( ); } // 在成员函数中调用虚函数
};
class C: public B {
public:
C( ) { fun( ); }
void fun( )
{ cout << "C::fun" << '\n'; }
};
int main( )
{ C c; //依次调用A、B、C三类的缺省构造函数
c.g( );
return 0;
}
?
#include <iostream>
using namespace std;
class Base {
public:
virtual ~ Base();
};
Base::~Base() {
cout<< "Base destructor" << endl;
}
class Derived: public Base{
public:
Derived();
~Derived();
private:
int *p;
};
Derived::Derived() {
p = new int(0);
}
Derived::~Derived() {
cout << "Derived destructor" << endl;
delete p;
}
void fun(Base* b) { //普通函数
cout<<"进入普通函数"<<endl;
delete b;
cout<<"退出普通函数"<<endl;
}
int main() {
//第一种方法:使用基类指针
Base *b = new Derived(); //基类对象指针,指向基类对象的地址
//Base *b = new Base(); //基类对象指针,指向基类对象的地址
delete b; //启动析构函数
cout<<endl;
//第二种方法:使用基类的引用
Base &p1=*(new Derived); //通过基类的引用来引用基类对象
delete &p1;
return 0;
}
?
?2.4、抽象类
如果一个类中至少有一个纯虚函数, 那么这个类被称为抽象类
#include <iostream>
using namespace std;
class container
{
protected:
double radius;
public:
container(double radius)
{
container::radius = radius;
}
virtual double surface_area() = 0; //纯虚函数
virtual double volume() = 0; //纯虚函数
};
class cube :public container //定义正方体类
{
public:
cube(double radius) :container(radius){};
double surface_area(); //虚函数在本类中的声明版本
double volume();
};
class sphere :public container //定义球体类
{
public:
sphere(double radius) :container(radius){}; //虚函数在本类中的声明版本
double surface_area();
double volume();
};
class cylinder :public container //定义圆柱体类
{
double height;
public:
cylinder(double radius, double height) :container(radius)
{
cylinder::height = height;
}
double surface_area(); //虚函数在本类中的声明版本
double volume();
};
double cube::surface_area()
{
return(radius*radius * 6);
}
double cube::volume()
{
return(radius*radius*radius);
}
double sphere::surface_area()
{
return 4 * 3.1416*radius*radius;
}
double sphere::volume()
{
return 3.1414*radius*radius*radius * 4 / 3;
}
double cylinder::surface_area()
{
return 3.1416 * 2 * radius*(height + radius);
}
double cylinder::volume()
{
return 3.1416*radius*radius*height;
}
int main()
{
container *ptr; //定义抽象类指针ptr
cube obj1(5); //创建正方体对象obj1
sphere obj2(5); //创建球体对象obj2
cylinder obj3(5, 5); //创建圆柱体对象obj3
ptr = &obj1; //指针ptr指向正方体对象obj1
cout << "The surface_area of cube is: " << ptr->surface_area() << endl;
cout << "The volume of cube is: " << ptr->volume() << endl;
ptr = &obj2; //指针ptr指向球体对象obj2
cout << "The surface_area of sphere is: " << ptr->surface_area() << endl;
cout << "The volume of sphere is: " << ptr->volume() << endl;
ptr = &obj3; //指针ptr指向圆柱体对象obj3
cout << "The surface_area of cylinder is: " << ptr->surface_area() << endl;
cout << "The volume of cylinder is: " << ptr->volume() << endl;
return 0;
}
?
|