基本概念:

采用函数(或过程)来描述对数据结构的操作,数据之间通过全局变量或参数传递进行联系

面向对象程序设计的特征是封装性、继承性和多态性

面向对象的程序主要由类和对象组成。

类的构成

类(class)是面向对象系统中最基本的组成元素,是一种自定义数据类型

类定义格式

class 类名
{
    public:
    //公有数据成员和公有成员函数;
    private:
    //私有数据成员和私有成员函数;
    protected:
    //保护数据成员和保护成员函数;
}


一般情况下:
– 类的数据成员都定义为private
– 把访问数据的成员函数定义为public

class A
{
    private:
    float x, y;
    public:
    void Setxy(float a,float b)
    { x=a; y=b; }
    void Print(void)
    { cout<<x<<‘\t’<<y<<endl; }
};

在类外不能直接使用 x 或 y ,必须通过Setxy()给 x 或 y 赋值,通过Print()输出 x 或 y

类内函数定义

class Clock
{
    public:
    隐式: 类内定义的函数
    void setTime(int h=0, int m=0,int s=0)
    { 
        Hour=h; Minute=m; Second=s;
    }
    void showTime();
    private:
    int Hour, Minute, Second;
};
显式:在类外用inline限定
inline void Clock::showTime()
{ 
    cout<<hour<<":"<<minute<<":"<<second<<endl; 
}

LIO-SAM类的变量和函数定义

private:
// imu队列、odom队列互斥锁
    std::mutex imuLock;
    std::mutex odoLock;
public:
 //成员函数
 //构造函数:一些成员变量的订阅和发布
 //数据处理的一些函数

对象

对象是类的实例或实体;

表面上看对象是某个“类” 类型的变量,但对象又不;是普通的变量, 对象是一个数据和操作的封装体。

对象定义

class Date
{
    int year;
    int month;
    int day;
} d1,d2;
class Date
{
    int year;
    int month;
    int day;
} ;

Date d1,d2;

对象内存存储


用不同的存储空间来存放每个对象的数据成员

同类的不同对象之间共享这个成员函数空间。

仅仅指的是该对象数据成员所占用的存储空间

类成员访问权限


public
任何外部函数都可以访问公有数据成员和成员函数

对象名和成员运算符访问对象的成员: 对象名.公有成员
指向对象的指针访问对象中的成员: 对象指针->公有成员
对象的引用访问对象中的成员: &引用变量名=对象名

private
只允许本类中的成员函数访问类定义体内的私有数据成员、私有成员函数 ,任何外部函数(友元函数除外)都不能访问类体内的私有成员

class Date
{
private:
    int year;
    int month;
    int day;
public:
    void input();
    void output();
} ;

void Date::input() {
    cout<<"请输入一个合法的年月日: ";
    cin>>year;
    cin>>month;
    cin>>day;
}
int main()
{
    Date d;
    d.input();
    d.output();
    return 0;
}

在成员函数的定义中,可以直接使用该类的所有成员,不必使用圆点运算符

构造函数

构造函数( Constructor)是一种特殊的成员函数,它是用来完成在声明对象的同时,对对象中的数据成员进行初始化

构造函数基本形式
如果类没有定义构造函数,系统会自动生成一个默认的构造函数。这个构造函数不含有参数,也
不会对数据成员进行初始化

实例代码

#include<iostream>
using namespace std;

//类的定义
class Clock{
public:
    Clock(); //默认构造函数
    Clock(int newH, int newM, int newS);//带参数的构造函数

    void setTime(int newH, int newM, int newS);
    void showTime();
private:
    int hour, minute, second;
};
//构造函数的实现:
Clock::Clock()  //默认构造函数
{
    hour = 0;
    minute = 0;
    second = 0;  //如果不赋值的话,会产生初值:0、0、1 
}
Clock::Clock(int newH, int newM, int newS) //带参构造函数
{
    hour = newH;
    minute = newM;
    second = newS;
}

//成员函数的实现
void Clock::setTime(int newH, int newM, int newS) {
    hour = newH;
    minute = newM;
    second = newS;
}
void Clock::showTime() {
    cout << hour << ":" << minute << ":" << second << endl;
}
//对象的使用
int main() {
    Clock c1;             //调用无参数的构造函数
    Clock c2(0, 0, 0);    //调用有参数的构造函数
    Clock c3(15, 30, 0);  //调用有参数的构造函数

    c1.showTime();
    c2.showTime();
    c3.showTime();

    return 0;
}

拷贝构造函数

用一个已定义并初始化的对象进行同一类的其他对象的构建及初始化。

首先,它是一个构造函数,当创建一个新对象时,系
统自动调用它;
– 其次,它的特殊功能是将参数代表的对象逐域拷贝到
新创建的对象中。
即用一个已有对象初始化一个正在建立的同类对象

#include <iostream>
using namespace std;
class point
{
    int x, y;
public:
    point(int vx, int vy) //构造函数定义
    {
        x = vx; y = vy;
        cout<<"构造函数!"<<endl; 
    }
    point(const point & p) //拷贝构造函数定义
    {
        x = p.x; y = p.y;
        cout<<"拷贝构造函数!"<<endl; 
    } 
    void print()  //成员函数定义
    {
        cout << x << " " << y << endl;
    }
};
int main()
{
    point p1(10, 20); //定义一个point类对象p1
    point p2(p1); //定义一个point类对象p2。

    point p3 = p1; 
       //也可以用赋值的方式使用拷贝构造函数
    p1.print();
    p2.print();
    p3.print();
    return 0;
}

浅拷贝与深拷贝
一般情况下使用编译器默认的拷贝构造函数就可以了,不需要自己写拷贝构造函数。

特殊情况下,如需在拷贝的同时修改数据成员的值、或需实现深拷贝等,则需自己编写(编译器默认的拷贝构造函数不能实现这些功能)

当类中有动态申请的数据空间时, 必须定义拷贝构造函数。

当类的数据成员中有指针类型时,就必须定义一个特定的
拷贝构造函数,该拷贝构造函数:
– 不仅可以实现原对象和新对象之间数据成员的拷贝,
– 而且可以为新的对象分配单独的内存资源,
– 这就是深拷贝构造函数

则必须在类中显式地定义一个完成拷贝功能的构造函数,以便实现成员数据的复制。
同时应显式定义相应的析构函数, 撤消动态分配的空间。

lass Student
{
    char *Name;                      // 姓名,注意:用指针实现
    int Age;                          // 年龄
public:
    Student(char *namep, int age)       // 构造函数
    {
        Age = age;
        if (namep)                        // 在构造函数中,动态申请空间
        {
            Name = new char[strlen(namep) + 1]; // A
            strcpy(Name, namep);
        }
        else Name = NULL;
    }
    Student(const Student &s); //拷贝构造函数声明 

    ~Student()    // 因为在构造函数中动态申请了空间,则在析构函数中,需释放空间
    {
        if (Name) delete[] Name;
    }
    void Show()
    {
        cout << Name << ',' << Age << endl;
    }
};

int main()
{
    Student a("George", 20);        
    a.Show(); 
    Student b = a;                 

    b.Show();
    return 0;
}

/*=================================================================

Student::Student(const Student &s)  //"浅"拷贝构造函数
{ 
    Name = s.Name;                      // 注意:直接赋地址值
    Age = s.Age;                        // 
}
=================================================================*/

Student::Student(const Student &s)   //"深"拷贝构造函数
{
    Age = s.Age;
    if(s.Name)
    {
        Name = new char[strlen(s.Name)+1];  // F
        strcpy(Name, s.Name);
    }
    else Name = NULL; 
}

注意最后一段先申请内存空间再赋值

Student::Student(const Student &s)   //"深"拷贝构造函数
{
    Age = s.Age;
    if(s.Name)
    {
        Name = new char[strlen(s.Name)+1];  // F
        strcpy(Name, s.Name);
    }
    else Name = NULL; 
}

构造函数的重载

有时在一个类中可以有几个构造函数,每个适应于不同的场合,这些函数之间以它们所带参数的个数或类型的不同区分。这种情况也称为重载构造函数

class x
{ //…
    public:
    x();
    x(int);
    x(int,char);
    x(float,char);
    //…
};
 main()
{ 
    x a; //调用无参构造函数
    x b(1); //调用一个参数的构造函数
    x c(1,’c’); //调用第三个构造函数
    x d(3.5,’d’); //调用第四个构造函数
}

析构函数

与构造函数相反, 完成对象被删除前的一些清理工作:

功能:当对象的生命期即将结束时( 系统释放对象所占的空间之前) , 系统会调用该对象的
析构函数, 析构函数执行完后, 系统释放该对象所占用的内存空间。

在对象中如果有使用new申请的空间,且当对象空间被回收前仍未被释放的,应当在析构函
数中,使用delete释放该空间

~ImageProjection(){}

类的组合

类A中的数据成员是另一个类B的对象。则该类A的对象称为组合对象–可以在已有抽象的基础上实现更复杂的抽象。

// 8-4-1.组合类.cpp 
// 构造函数和析构函数的执行顺序 
#include <iostream>
#include <cmath>
using namespace std;

class Point { //Point类定义
public:
    Point(int xx = 0, int yy = 0) //构造函数
    {
        x = xx;
        y = yy;
        cout << "Calling the  constructor of Point "<< endl;
    }
    Point(Point &p); //拷贝构造函数声明
    int getX() { return x; }
    int getY() { return y; }
private:
    int x, y;
};
Point::Point(Point &p) { //拷贝构造函数的实现
    x = p.x;
    y = p.y;
    cout << "Calling the copy constructor of Point " <<  endl; 

}
//类的组合,包含了类Point的对象 
class Line { //Line类的定义
public: 
    Line(Point xp1, Point xp2); //构造函数声明,形参是Point类的对象
    Line(Line &l);  //拷贝构造函数声明
    double getLen() { return len; } //成员函数声明
private: //私有数据成员
    Point p1, p2;  
    double len;
};
//组合类的构造函数定义
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2) {
    cout << "Calling constructor of Line" << endl;

    double x = (double)(p1.getX() - p2.getX());
    double y = (double)(p1.getY() - p2.getY());

    len = sqrt(x * x + y * y);
}
Line::Line(Line &l) : p1(l.p1), p2(l.p2) {//组合类的拷贝构造函数
    cout << "Calling the copy constructor of Line" << endl;
    len = l.len;
}
//主函数
int main() {
    Point myp1(1, 1), myp2(4, 5); //建立Point类的对象
    Line line1(myp1, myp2); //建立Line类的对象
    Line line2(line1); //利用拷贝构造函数建立一个新对象
    cout << "The length of the line1 is: ";
    cout << line1.getLen() << endl;
    cout << "The length of the line2 is: ";
    cout << line2.getLen() << endl;
    return 0;
}

构造函数和析构函数的执行顺序

构造函数的执行次序是先遇到哪个构造函数,就执行哪个。

析构函数的执行次序恰好和构造函数相反。

#include <iostream>
#include <cmath>
using namespace std;

class Point { //Point类定义
public:
    Point(int xx = 0, int yy = 0) //构造函数
    {
        x = xx;
        y = yy;
        cout << "Calling the  constructor of Point "<< endl;
    }
    Point(Point &p); //拷贝构造函数声明
    int getX() { return x; }
    int getY() { return y; }
private:
    int x, y;
};
Point::Point(Point &p) { //拷贝构造函数的实现
    x = p.x;
    y = p.y;
    cout << "Calling the copy constructor of Point " <<  endl; 

}
//类的组合,包含了类Point的对象 
class Line { //Line类的定义
public: 
    Line(Point xp1, Point xp2); //构造函数声明,形参是Point类的对象
    Line(Line &l);  //拷贝构造函数声明
    double getLen() { return len; } //成员函数声明
private: //私有数据成员
    Point p1, p2;  
    double len;
};
//组合类的构造函数定义
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2) {
    cout << "Calling constructor of Line" << endl;

    double x = (double)(p1.getX() - p2.getX());
    double y = (double)(p1.getY() - p2.getY());

    len = sqrt(x * x + y * y);
}
Line::Line(Line &l) : p1(l.p1), p2(l.p2) {//组合类的拷贝构造函数
    cout << "Calling the copy constructor of Line" << endl;
    len = l.len;
}
//主函数
int main() {
    Point myp1(1, 1), myp2(4, 5); //建立Point类的对象
    Line line1(myp1, myp2); //建立Line类的对象
    Line line2(line1); //利用拷贝构造函数建立一个新对象
    cout << "The length of the line1 is: ";
    cout << line1.getLen() << endl;
    cout << "The length of the line2 is: ";
    cout << line2.getLen() << endl;
    return 0;
}


前向引用声明

如果需要在某个类的声明之前,引用该类,则应进行前向引用声明。– 可以声明一个类而不定义它。这个声明,有时候被称为前向声明(forwarddeclaration)。

class B; //前向引用声明
class A {
public:
    void f(B b);
};
class B {
public:
    void g(A a);
};

对象数组

对象数组就是数组里的每个元素都是类的对象

#include <iostream>
using namespace std;

class Point { //类的定义
public: //外部接口
    Point(int m=0, int n=0);
    ~Point(){} ;
    void move(int newX, int newY){
        x=newX; y=newY;
    };
    int getX() const { return x; }
    int getY() const { return y; }

private: 
    int x, y;
};
Point::Point(int m, int n):x(m),y(n) {
}

int main() 
{
    cout << "Entering main..." << endl;
    Point a[2];
    cout<<"a[0]: x ="<<a[0].getX() <<"  , y =" <<a[0].getY()<<endl;
    cout<<"a[1]: x ="<<a[1].getX() <<"  , y =" <<a[1].getY()<<endl;
    for (int i = 0; i < 2; i++)
    {
        a[i].move(i + 10, i + 20);
        cout<<"a["<<i<<"]: x ="<<a[i].getX() <<"  , y =" <<a[i].getY()<<endl;
    }
    cout << "Exiting main..." << endl;
    return 0;
}

对象指针

建立对象时,编译系统为每一个对象分配一定的存储空间,用来存放其数据成员。
对象的起始地址即是对象的指针

定义


访问

用对象指针访问对象数据成员的格式为:
对象指针名->数据成员;
• 用对象指针访问对象成员函数的格式为:
对象指针名->成员函数(实参列表);

#include <iostream>
using namespace std;

class Point {
public:
    Point(int x = 0, int y = 0) : x(x), y(y) { }
    int getX() const { return x; }
    int getY() const { return y; }
private:
    int x, y;
};
int main() {
    Point a(4, 5);    
    Point *p1 = &a; //定义对象指针,用a的地址初始化

    cout << p1->getX() <<"  ,  "<<p1->getY()<< endl;//用指针访问对象成员
    cout << (*p1).getX() <<"  ,  "<<(*p1).getY()<< endl; //用对象名访问对象成员
    cout << a.getX() <<"  ,  "<<a.getY()<< endl; //用对象名访问对象成员
    return 0;
}

This指针

this指针是隐含在类的每一个非静态成员函数中的一个对象指针, 它是一个指向正操作该成员函数的对象

该指针是系统自动产生的, 不需要定义;– 并且它是常量指针,不能被赋值和修改,只能
使用它(即this指针的值不能改变,它指向的值可以改变)

以下两个代码是等价的

#include <iostream>
using namespace std;
class  Sample
{ 
    int x, y; 
public: 
   Sample( int a=0, int b=0) 
   { x=a; y=b; } 
   void Print( ) 
   {       cout << x <<'\n'; 
           cout << y <<'\n'; 
   }
};
int main( )
{      Sample c2(3, 5); 
       c2.Print( );
        return 0;
}
#include <iostream>
using namespace std;
class  Sample
{  int x, y;
public: 
    Sample( int a=0, int b=0)  
   {       this->x = a;             
           this->y = b; 
           cout<<"this的值 = "<< this <<endl; 
    }
   void Print( )
   {        cout << (this->x) <<'\n';   
            cout << (this->y) <<'\n'; 
   } 
}; 
int main( )
{        Sample c2(3, 5); 
         cout<<"c2的地址 = "<<&c2<<endl;
         Sample c3(8, 5); 
         cout<<"c3的地址 = "<<&c3<<endl;
         c2.Print( ); 
         return 0;
}

向函数传递对象

值传递
作为实参的对象的值复制给形参创建的局部对象,这种传递是单向的,只从实参
到形参。
因此,函数对形参值做的改变不会影响到实参

#include <iostream>
using namespace std;
class Square{
private:
    double length;
public:
    Square(double len);
    void Add(Square s);   //对象作为函数的参数 
    void Outpout();
};
Square::Square (double len):length(len)
{
}
void Square::Add (Square s)
{
    s.length =s.length +1.0;      //给形参对象的length数据成员加1
}                                 //对该形参的改变不会改变实参 
void Square::Outpout()
{
    cout<<"Square Area:"<<length * length<<endl;
}


int main()
{
    Square s(2.5);
    cout<<"add before"<<endl;
    s.Outpout ();
    s.Add (s);
    cout<<"add after"<<endl;
    s.Outpout ();
    return 0;
}

指针传递

对象指针作为参数传递的是地址。也就是说实参向形参传递的是实参所指向对象的地址。
– 实参对象指针变量和形参对象指针变量指向同一内存地址,因而作为形参的对象,其值的改变,也就是改变了实参对象的值,所以指针传递是一种双向传递

#include <iostream>
using namespace std;
class Square{
private:
    double length;
public:
    Square(double len);
    void Add(Square *s);  //对象指针作为函数参数 
    void Outpout();
};
Square::Square (double len):length(len)
{
}
void Square::Add (Square *s)
{
    s->length =s->length +1.0;           //给形参对象的length数据成员加1
}
void Square::Outpout()
{
    cout<<"Square Area:"<<length * length<<endl;
}

int main()
{
    Square s(2.5);
    cout<<"add before"<<endl;
    s.Outpout ();
    s.Add (&s);
    cout<<"add after"<<endl;
    s.Outpout ();
    return 0;
}

引用传递

采用了引用方式进行参数传递, 形参对象就相当于是实参对象的“ 别名” , 对形参
的操作其实就是对实参的操作。
• 使用对象引用作为函数参数不但有指针作为参数的优点, 而且比指针作为参数更简
单、 更直接。

#include <iostream>
using namespace std;
class Square{
private:
    double length;
public:
    Square(double len);
    void Add(Square &s);  //对象的引用作为函数的参数 
    void Outpout();
};
Square::Square (double len):length(len)
{
}
void Square::Add (Square &s)
{
    s.length =s.length +1.0;           //给形参对象的length数据成员加1
}
void Square::Outpout()
{
    cout<<"Square Area:"<<length * length<<endl;
}

int main()
{
    Square s(2.5);
    cout<<"add before"<<endl;
    s.Outpout ();
    s.Add (s);
    cout<<"add after"<<endl;
    s.Outpout ();

    return 0;
}