【面向对象编程】
【面向对象编程】
基本概念
对象
属性(数据)
方法(操作)
继承
允许一个类从另一个类继承属性和方法->实现代码重用
eg 创建一个电动车类,继承自汽车类,并添加特有的属性,如电池容量,并仍然拥有汽车的特性,比如速度
多态
允许不同类的对象以相同的方式调用相同的方法
eg 汽车类和电动车类都可以有一个行驶方法
封装
限制外部访问 可以保护对象的数据不被随意修改
代码示例
#include<string>
#include"rclcpp/rclcpp.hpp"
class PersonNode : public rclcpp::Node//继承父类
{
private:
std::string name_;
int age_;
public:
//构造函数
PersonNode(const std::string &node_name,
const std::string &name,
const int &age): Node(node_name)//继承父类函数
{
this->name_=name;
this->age_=age;
};
void eat(const std::string &food_name)
{
RCLCPP_INFO(this->get_logger(),"我是%s,今年%d岁,我现在正在吃%s",
name_.c_str(),age_,food_name.c_str());
};
};
int main(int argc,char** argv){
rclcpp::init(argc,argv);
//构造函数
auto node=std::make_shared<PersonNode>("cpp_node","麦麦",18);
node->eat("vw50");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
访问权限
public:公共权限,允许内外部直接访问
private:私有权限,只允许内部访问
protected:保护权限,跟私有类似,但在继承中与私有不同
类
初始化
【定义】
pubilc:
Student(int ID, std::string Name): id(ID), name(Name) {}
【调用】
Student s1(114514, “pikachu”);
析构函数
在每次删除所创建的对象时执行
不编写析构函数,编译器自动会生成一个默认的析构函数
~Student();
复制构造函数
使用同一类中之前创建的对象来初始化新创建的对象
Student(const Student &obj){};
this指针
指向对象地址本身的指针
class Student{
public:
void changeName(std::string name){
this->name=name;
}
}
引用&
:和指针类似
直接指向内存中某个具体的对象
int main(){
Student stu1(114514,"pikachu");
Student &stu2=stu1;
stu1.display();
stu2.display();
stu2.changeName("asd");
stu1.display();
}
改变stu2就是改变stu1
!!!在编写函数参数时,如果需要使用一个类来作为参数,建议使用引用
优点:
(1)不使用引用,在传入时对象会被复制一份
如果需要操作被传入的对象,可能会出现问题,比如源对象没被修改
(2)避免复制,节约空间
继承:公有继承,保护继承,私有继承
class Derive:public Base{} //创建一个Derive类,公有继承Base类
class Derive:Base{} //默认私有继承
公有继承
基类和派生类相同属性
保护继承
基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问
私有继承
即所有基类成员均变成派生类的私有成员
运算符重载:operator
class list{
public:
list(int a,int b):a(a),b(b){}
list operator+(const list &l){
return list(a+l.a,b+l.b);//只需要一个l.a
};
list operator-(const list &l){
return list(a-l.a,b-l.b);
};
void show(){
std::cout<<"a="<<a<<" b="<<b<<std::endl;
}
private:
int a;
int b;
};
int main(){
list l1(1,2);
list l2(4,5);
list l3=l1+l2;//1+4=5 2+5=7
list l4=l1-l2;
}
内存管理
int *p = new int(114); //申请一个int类型的内存,并赋值
int *array = new int[100000]; // 申请十万个 int 类型的内存
delete [] array; // 释放内存
智能指针
std::shared_ptr p1(new int(1)); //p1 是一个指向 int 类型的智能指针
std::cout<<*p1<<“\n”; //使用方式跟普通指针没什么区别
共享指针shared_ptr:可以有多个 shared_ptr 指向一个对象,当最后一个 shared_ptr 销毁时,对象也会被销毁
独占指针unique_ptr:独立管理一个对象的生命周期,只有一个 unique_ptr 指向一个对象,当 unique_ptr 销毁时,对象也会被销毁
弱指针weak_ptr:弱引用,通常与 shared_ptr 搭配,不会增加对象的引用数,当最后一个shared_ptr 销毁时,对象也会被销毁
共享指针
【创建】
auto cat_p = make_shared<Cat>("cat333");
cat_p->speak();
shared_ptr<Cat> cat1=cat; //复制,然后计数器加一
cat1=nullptr; //删除指针,然后计数器减一
Copy则计数器+1,销毁则计数器-1
使用use_count()
查看计数器
独占指针
独占指针需要使用std::move()
来移交内存拥有权,不能被复制,只能被移动
在形参使用引用
// 从原始指针创建,注意需要销毁原始指针
auto cat_p1=new Cat("cat111"); // 创建原始指针
unique_ptr<Cat> cat_p2(cat_p1); // 创建独占指针
cat_p1=nullptr; // 原始指针置空
delete cat_p1; // 释放原始指针
cat_p2->speak();
// 直接创建
unique_ptr<Cat> cat_p1(new Cat(“cat222"));
cat_p1->speak();
// 使用函数创建(推荐)
auto cat_p = make_unique<Cat>("cat333");
cat_p->speak();
弱指针
(1)没有所有权,不能调用-> 和 解引用*
(2)解决循环依赖问题:A对象需要存储其他A对象的信息
(3)不能单独存在
constexpr
关键字
(1)编译期就能得到计算结果->在用来提高性能和检查错误时会有帮助
(2)可以使用 constexpr 代替 #define 定义常量
(3)constexpr也可以用来申明函数,支持递归
匿名函数:Lambda 表达式
[捕获列表] (参数列表) -> 返回类型 { 函数体 ;};
#include<iostream>
#include<algorithm>
int main(){
auto add=[](int a,int b) -> int {return a+b;};
int sum=add(200,50);
auto print_sum=[sum]()->void{std::cout<<sum<<std::endl;};
print_sum();
return 0;
}
函数包装器std::function
存储任意可调用对象(函数、函数指针、Lambda表达式)并提供统一调用接口
std::function<void(const std::string &)>
std::bind:将一个成员函数变成一个std::function的对象
正常调用成员函数:file_save.save_with_member_fun
->将FileSave::save_with_member_fun与对象file_save绑定在一起
std::placeholders::_1 占位符预留1个位置传递函数参数
示例
#include<iostream>
#include<functional>
void save_with_free_fun(const std::string &file_name)
{
std::cout<<"调用自由函数,保存"<<file_name<<std::endl;
}
class FileSave
{
public:
void save_with_member_fun(const std::string &file_name)
{
std::cout<<"调用成员方法,保存"<<file_name<<std::endl;
};
};
int main()
{
FileSave file_save;
auto save_with_lambda_fun=[](const std::string &file_name) -> void
{
std::cout<<"调用Lambda函数,保存"<<file_name<<std::endl;
};
//将自由函数放入function对象
std::function<void(const std::string &)> save1=save_with_free_fun;
//将Lambda函数放入function对象
std::function<void(const std::string &)> save2=save_with_lambda_fun;
//将成员方法放入function对象
//std::function<void(const std::string &)> save3=
std::bind(&FileSave::save_with_member_fun,&file_save,
std::placeholders::_1);
//调用形式统一
save1("file.txt");
save2("file.txt");
save3("file.txt");
return 0;
}
多线程与回调函数
std::this_thread 当前线程
示例
#include<iostream>
#include<chrono>//时间相关
#include<thread>//线程相关
#include<functional>//函数包装器
class Download
{
public:
//传入两个参数:std::function->函数
void download(const std::string &host,const std::string &path,
const std::function<void(const std::string &,
const std::string &)> &callback)
//callback:回调函数:请求成功后调用,传递请求结果
{
std::cout<<"线程ID:"<<std::this_thread::get_id()<<std::endl;
httplib::Client client(host);
auto response=client.Get(path);
if(response && response->status == 200)//执行回调函数
{
callback(path,response->body);
}
}
//包装download函数->添加多线程
void sart_download(const std::string &host,const std::string &path,
const std::function<void(const std::string &,const std::string &)>
&callback)
{ //当前类的指针
auto download_fun = std::bind(&Download::download,this,
std::palceholders::_1,std::palceholders::_2,std::palceholders::_3);
//创建thread对象
std::thread download_thread(download_fun,host,path,callback);
//将线程与当前进程分离->线程在后台运行
download_thread.detach();
}
}
int main()
{
Download download;
auto download_finish_callback = [](const std::string &path,
const std::string &result) -> void
{
std::cout<<"下载完成"...
};
//创建三个线程
download.start_download("...","...",download_finish_callback);
download.start_download("...","...",download_finish_callback);
download.start_download("...","...",download_finish_callback);
//延迟当前线程
std::this_thread::sleep_for(std::chrono::milliseconds(1000*10));
return 0;
}