SLAM源码解析过程中C++知识点梳理(持续更新中...)

总结在算法中常见的C++知识点,用于学习记录

1、封装

  • 封装的意义:
    (1)将属性和行为作为一个整体,表现生活中的事物
    (2)将属性和行为加以权限控制(public公共权限、struct保护权限、private私有权限)

语法:class 类名{ 访问权限: 属性 / 行为 };

属性:成员属性/成员变量
行为:成员函数/成员方法

//学生类
class Student {
public:
	void setName(string name) {
		m_name = name;
	}
	void setID(int id) {
		m_id = id;
	}

	void showStudent() {
		cout << "name:" << m_name << " ID:" << m_id << endl;
	}
public:
	string m_name;
	int m_id;
};

int main() {

	Student stu;
	stu.setName("德玛西亚");
	stu.setID(250);
	stu.showStudent();

	system("pause");

	return 0;
}


2、继承

  • 作用:减少重复代码
    class A :public B;

语法:class 子类 : 继承方式 父类

3、多态

静态多态(早绑定): 函数重载 和 运算符重载属于静态多态,复用函数名
动态多态(晚绑定): 派生类和虚函数实现运行时多态

多态满足条件:
(1)有封装和继承关系
(2)子类重写父类中的虚函数
多态使用条件:父类指针或引用指向子类对象

class Animal
{
public:
	//Speak函数就是虚函数
	//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};

class Cat :public Animal
{
public:
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};

class Dog :public Animal
{
public:

	void speak()
	{
		cout << "小狗在说话" << endl;
	}

};
//我们希望传入什么对象,那么就调用什么对象的函数
//如果函数地址在编译阶段就能确定,那么静态联编
//如果函数地址在运行阶段才能确定,就是动态联编

void DoSpeak(Animal & animal)
{
	animal.speak();
}
//
//多态满足条件: 
//1、有继承关系
//2、子类重写父类中的虚函数
//多态使用:
//父类指针或引用指向子类对象

void test01()
{
	Cat cat;
	DoSpeak(cat);


	Dog dog;
	DoSpeak(dog);
}


int main() {

	test01();

	system("pause");

	return 0;
}

4、函数重载

  • 作用:函数名相同,提高复用性

满足条件:
(1)同一个作用域下
(2)函数名称相同
(3)函数参数类型不同、个数不同、顺序不同

5、运算符重载

(1)加号运算符重载
(2)左移运算符重载
(3)递增运算符重载
(4)赋值运算符重载
(5)关系运算符重载

6、引用

  • 作用:给变量起别名

语法:数据类型 &别名 = 原名

int &b = a;

三种传递
(1)值传递:形参不会修饰实参
(2)地址传递:形参会修饰实参
(3)引用传递:形参会修饰实参

//1. 值传递
void mySwap01(int a, int b) {
	int temp = a;
	a = b;
	b = temp;
}

//2. 地址传递
void mySwap02(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}

//3. 引用传递
void mySwap03(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

int main() {

	int a = 10;
	int b = 20;

	mySwap01(a, b);
	cout << "a:" << a << " b:" << b << endl;

	mySwap02(&a, &b);
	cout << "a:" << a << " b:" << b << endl;

	mySwap03(a, b);
	cout << "a:" << a << " b:" << b << endl;

	system("pause");

	return 0;
}
  • 引用的本质:在C++内部实现一个指针常量
//发现是引用,转换为 int* const ref = &a;指针的指向不可以改,指针指向的值可以改
void func(int& ref){
	ref = 100; // ref是引用,转换为*ref = 100
}

7、内存四区

(1)代码区:存放函数体的二进制代码,操作管理系统
(2)全局区:存放全局变量、静态变量和常量(除const修饰的局部变量)
(3)栈区:由编译器自动分配释放,存放函数的参数值、局部变量等
(4)堆区:由程序员分配释放,若程序不释放,程序结束时由操作系统回收,在C++主要利用new在堆区开辟内存

8、new操作符

new 是一个运算符,用于在动态内存中分配空间以创建对象,并返回指向该对象的指针。它通常用于创建动态对象,这些对象的生存期不受限制,并且可以在程序的任何位置进行释放。

用法:

T *ptr = new T;
  • T 是要创建的对象类型。
  • ptr 是一个指针,指向新对象的地址

示例:使用 new 来动态分配一个整数和一个自定义类的对象

// 动态分配一个整数
int *intPtr = new int;
*intPtr = 42;

// 动态分配一个自定义类的对象
class MyClass {
public:
    MyClass(int value) : data(value) {}
    void print() { std::cout << "Value: " << data << std::endl; }
private:
    int data;
};

MyClass *objPtr = new MyClass(10);
objPtr->print();

// 记得在不再需要时释放动态分配的内存
delete intPtr;
delete objPtr;

在动态分配内存后,你需要负责手动释放这些内存以避免内存泄漏。释放动态分配的内存使用 delete 运算符

9、构造函数和析构函数

(1)构造函数:进行初始化操作
(2)析构函数:进行清理的操作

构造函数语法:类名(){}

析构函数语法: ~类名(){}

拷贝构造函数:

class Person {
public:
//拷贝构造函数
	Person(const Person& p) {
		age = p.age;
		cout << "拷贝构造函数!" << endl;
	}
};

10、深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作

总结:一个构造函数里面可以有浅拷贝和深拷贝,堆区拷贝是深拷贝,简单赋值是浅拷贝

class Person {
public:
	//无参(默认)构造函数
	Person() {
		cout << "无参构造函数!" << endl;
	}
	//有参构造函数
	Person(int age ,int height) {
		
		cout << "有参构造函数!" << endl;

		m_age = age;
		m_height = new int(height);
		
	}
	//拷贝构造函数  
	Person(const Person& p) {
		cout << "拷贝构造函数!" << endl;
		//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
		m_age = p.m_age;
		m_height = new int(*p.m_height);
		
	}

	//析构函数
	~Person() {
		cout << "析构函数!" << endl;
		if (m_height != NULL)
		{
			delete m_height;
		}
	}
public:
	int m_age;
	int* m_height;
};

void test01()
{
	Person p1(18, 180);

	Person p2(p1);

	cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;

	cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

11、static

(1)修饰普通变量:修改变量的存储区域和生命周期,使变量存储在静态区。
(2)修饰普通函数:表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为
了防止与他人命令函数重名,可以将函数定位为 static。
(3)修饰静态成员变量: 所有对象共享同一份数据、在编译阶段分配内存、类内声明,类外初始化
(4)修饰静态成员函数:所有对象共享同一个函数、静态成员函数只能访问静态成员变量

静态成员变量
class Person
{
public:
	static int m_A; //静态成员变量
private:
	static int m_B; //静态成员变量也是有访问权限的
};

静态成员函数
class Person
{
public:
	static void func()
	{
		cout << "func调用" << endl;
		m_A = 100;
		//m_B = 100; //错误,不可以访问非静态成员变量
	}
	static int m_A; //静态成员变量
	int m_B; // 
private:

	//静态成员函数也是有访问权限的
	static void func2()
	{
		cout << "func2调用" << endl;
	}
};
int Person::m_A = 10;

void test01()
{
	//静态成员变量两种访问方式
	//1、通过对象
	Person p1;
	p1.func();
	//2、通过类名
	Person::func();
	//Person::func2(); //私有权限访问不到
}

12、Const

  • 作用:
    (1)修饰变量,说明该变量不可以被改变
    (2)修饰指针,包括常量指针、指针常量、既修饰指针,又修饰常量
    (3)常量引用,用来修饰形参,防止误操作
    (4)修饰成员函数,成员函数加const后为常函数,不可以修改成员变量
修饰指针
	//const修饰的是指针,指针指向可以改,指针指向的值不可以更改
	const int * p1 = &a; //常量指针
	p1 = &b; //正确
	//*p1 = 100;  报错
 
	//const修饰的是常量,指针指向不可以改,指针指向的值可以更改
	int * const p2 = &a;//指针常量
	//p2 = &b; //错误
	*p2 = 100; //正确
 
    //const既修饰指针又修饰常量
	const int * const p3 = &a;
	//p3 = &b; //错误
	//*p3 = 100; //错误

常量引用
//引用使用的场景,通常用来修饰形参
void showValue(const int& v) {
	//v += 10;
	cout << v << endl;
}

int main() {

	//int& ref = 10;  引用本身需要一个合法的内存空间,因此这行错误
	//加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;
	const int& ref = 10;

	//ref = 100;  //加入const后不可以修改变量
	cout << ref << endl;

	//函数中利用常量引用防止误操作修改实参
	int a = 10;
	showValue(a);
	system("pause");
	return 0;
}
修饰成员函数
class Person {
public:
	Person() {
		m_A = 0;
		m_B = 0;
	}

	//this指针的本质是一个指针常量,指针的指向不可修改
	//如果想让指针指向的值也不可以修改,需要声明常函数
	void ShowPerson() const {
		//const Type* const pointer;
		//this = NULL; //不能修改指针的指向 Person* const this;
		//this->mA = 100; //但是this指针指向的对象的数据是可以修改的

		//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
		this->m_B = 100;
	}

	void MyFunc() const {
		//mA = 10000;
	}

public:
	int m_A;
	mutable int m_B; //可修改 可变的
};


//const修饰对象  常对象
void test01() {

	const Person person; //常量对象  
	cout << person.m_A << endl;
	//person.mA = 100; //常对象不能修改成员变量的值,但是可以访问
	person.m_B = 100; //但是常对象可以修改mutable修饰成员变量

	//常对象访问成员函数
	person.MyFunc(); //常对象不能调用const的函数

}

int main() {

	test01();

	system("pause");

	return 0;
}

13、this指针

this 指针是一个隐含于每一个非静态成员函数中的特殊指针。this指针指向被调用的成员函数所属的对象

  • this指针的用途
    (1)当形参和成员变量同名时,可用this指针来区分
    (2) 在类的非静态成员函数中返回对象本身,可使用return *this
class Person
{
public:

	Person(int age)
	{
		//1、当形参和成员变量同名时,可用this指针来区分
		this->age = age;
	}

	Person& PersonAddPerson(Person p)
	{
		this->age += p.age;
		//返回对象本身
		return *this;
	}

	int age;
};

void test01()
{
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;

	Person p2(10);
	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
	cout << "p2.age = " << p2.age << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

14、friend(友元)

  • 作用:友元的目的就是让一个函数或者类 访问另一个类中私有成员

友元的关键字为 friend

友元的三种实现:
(1) 全局函数做友元
(2) 类做友元
(3) 成员函数做友元

类做友元
class Building;
class goodGay
{
public:

	goodGay();
	void visit();

private:
	Building *building;
};


class Building
{
	//告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容
	friend class goodGay;

public:
	Building();

public:
	string m_SittingRoom; //客厅
private:
	string m_BedRoom;//卧室
};

Building::Building()
{
	this->m_SittingRoom = "客厅";
	this->m_BedRoom = "卧室";
}

goodGay::goodGay()
{
	building = new Building;
}

void goodGay::visit()
{
	cout << "好基友正在访问" << building->m_SittingRoom << endl;
	cout << "好基友正在访问" << building->m_BedRoom << endl;
}

void test01()
{
	goodGay gg;
	gg.visit();

}

int main(){

	test01();

	system("pause");
	return 0;
}

15、namespace( 命名空间)

在C++中,namespace 是一种用于组织和管理代码的机制。它允许你将相关的变量、函数、类等封装在一个命名空间中,以避免命名冲突并提高代码的可维护性。

  • 以下是相关使用

(1)命名空间定义

使用namespace 关键字定义一个命名空间,将相关的代码放入其中。命名空间通常位于全局作用域内,但也可以嵌套在其他命名空间内。

namespace MyNamespace {
    // 变量、函数、类等代码放在这里
}

(2)使用命名空间

命名空间被定义,可以使用 :: 操作符来引用命名空间中的元素。例如,如果有一个函数 myFunction 在 MyNamespace 中,你可以这样调用它:

MyNamespace::myFunction();

(3)避免命名冲突

命名空间的一个主要作用是避免全局命名冲突。如果你有两个不同的库或代码文件,它们都有一个名为 myFunction 的函数,你可以将它们放入不同的命名空间,从而避免冲突。

(4)嵌套命名空间

namespace OuterNamespace {
    namespace InnerNamespace {
        // 变量、函数、类等代码放在这里
    }
}

(5)使用命名空间别名

为一个命名空间定义一个别名,以简化代码。这在较长或复杂的命名空间名称上特别有用。

namespace MyNS = MyNamespace;

(6)匿名命名空间

C++还提供了匿名命名空间,这是一个没有名字的命名空间,其内容在当前编译单元内是唯一的。它常用于定义仅在当前文件内可见的全局变量和函数

namespace {
    int x = 42; // 仅在当前文件内可见
}

16、enum(枚举)

枚举允许你为一组相关的整数常量定义具有有意义的名称,以提高代码的可读性和可维护性。

  • 用于以下情况:

(1)定义枚举类型:

enum Color {
    Red,
    Green,
    Blue
};

(2)显式赋值:为枚举常量分配整数值

enum Status {
    OK = 0,
    Error = 1,
    Unknown = 999
};

(3)使用枚举类型:声明变量,函数参数或函数返回值为枚举类型,以表示一组离散的状态或选项

Color chosenColor = Red;
Status systemStatus = OK;

(4)使用枚举值:进行比较和操作

if (chosenColor == Red) {
    // 执行红色相关的操作
}

(5)枚举的作用域

枚举类型和它的枚举常量都在定义它们的作用域内可见。通常,它们被定义在全局作用域或类的内部作用域中

(6)使用枚举类(C++11及以后)

C++11 引入了强类型的枚举,也称为枚举类。枚举类提供更严格的类型检查,防止错误的枚举值被使用。定义一个枚举类如下:

enum class Color {
    Red,
    Green,
    Blue
};

使用枚举类时,你需要指定枚举值的类型,例如:

Color chosenColor = Color::Red;

17、static_cast

static_cast 是 C++ 中的一个类型转换操作符,用于执行编译时类型转换。它可以将一个表达式的类型强制转换为另一个类型,前提是这种类型转换是安全的

  • 用于以下情况:

(1)基本数据类型之间的转换,例如将整数类型转换为浮点数类型

int integerNumber = 42;
double doubleNumber = static_cast<double>(integerNumber);

(2)子类指针或引用到基类指针或引用的转换:多态的情况下,将子类对象指针或引用转换为基类指针或引用。

class Base { /* ... */ };
class Derived : public Base { /* ... */ };

Derived derivedObj;
Base* basePtr = static_cast<Base*>(&derivedObj);

(3)枚举类型转换:使用 static_cast 来执行不同枚举类型之间的转换。

enum class Color { Red, Green, Blue };
int enumValue = static_cast<int>(Color::Green);

(4)显式类型转换

double pi = 3.14159265359;
int approximatedPi = static_cast<int>(pi);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以回答这个问题。在Ubuntu18.04,在rplidar_ros新建slam.launch文件的步骤如下: 1. 打开终端,输入以下命令安装rplidar_ros包: ``` sudo apt-get install ros-melodic-rplidar-ros ``` 2. 在终端输入以下命令,创建一个新的工作空间: ``` mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src catkin_init_workspace ``` 3. 在终端输入以下命令,将rplidar_ros包复制到工作空间: ``` cd ~/catkin_ws/src git clone https://github.com/robopeak/rplidar_ros.git ``` 4. 在终端输入以下命令,编译工作空间: ``` cd ~/catkin_ws catkin_make ``` 5. 在终端输入以下命令,打开slam.launch文件: ``` roscd rplidar_ros cd launch gedit slam.launch ``` 6. 在slam.launch文件添加以下内容: ``` <launch> <node pkg="gmapping" type="slam_gmapping" name="slam_gmapping"> <param name="base_frame" value="base_link"/> <param name="odom_frame" value="odom"/> <param name="map_frame" value="map"/> <param name="map_update_interval" value="1."/> <param name="maxUrange" value="10."/> <param name="sigma" value=".05"/> <param name="kernelSize" value="1"/> <param name="lstep" value=".05"/> <param name="astep" value=".05"/> <param name="iterations" value="5"/> <param name="lsigma" value=".075"/> <param name="ogain" value="3."/> <param name="lskip" value=""/> <param name="srr" value=".1"/> <param name="srt" value=".2"/> <param name="str" value=".1"/> <param name="stt" value=".2"/> <param name="linearUpdate" value=".2"/> <param name="angularUpdate" value=".1"/> <param name="temporalUpdate" value="3."/> <param name="resampleThreshold" value=".5"/> <param name="particles" value="30"/> <param name="xmin" value="-10."/> <param name="ymin" value="-10."/> <param name="xmax" value="10."/> <param name="ymax" value="10."/> <param name="delta" value=".05"/> <param name="llsamplerange" value=".01"/> <param name="llsamplestep" value=".01"/> <param name="lasamplerange" value=".005"/> <param name="lasamplestep" value=".005"/> <param name="tf_delay" value=".05"/> </node> <node pkg="rplidar_ros" type="rplidarNode" name="rplidarNode" output="screen"> <param name="serial_port" value="/dev/ttyUSB"/> <param name="serial_baudrate" value="115200"/> </node> </launch> ``` 7. 保存并关闭slam.launch文件。 8. 在终端输入以下命令,启动slam.launch文件: ``` roslaunch rplidar_ros slam.launch ``` 这样就可以在Ubuntu18.04在rplidar_ros新建slam.launch文件了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值