前言:
作为一名机械专业学生,在没有C++基础的情况下,直接网上的代码资源学习ROS收获不大。虽然老师们并不要求我们会写相关代码,但能看懂代码段的含义是非常必要的,因此在正式开始ROS学习应用前需要对C++有基本概念。
一、C++介绍:
1.面向过程程序设计的缺点:
(1)可读性不佳:在面向过程的程序设计中,函数及其所操作的数据结构没有直观的联系,导致随着程序规模的增加可读性下降。
(2)扩展性差:面向过程程序设计没有“封装”和“隐藏”的概念,要访问某个数据结构中的某个变量可以直接访问,但要修改该变量时需要将所有访问该变量的语句都找出来修改,且难以差错和重用代码。
2.C++的特点:
C++是C语言的继承,既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。
二、类和对象:
1.介绍类和对象:
(1)面向对象编程:
面向对象是相对于面向过程来讲的,面向对象思想是把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模。
面向对象有三大特性:封装、继承和多态。
C++中的类是实现面向对象编程的核心要素。
(2)类的介绍:
类是根据客观事物抽象而成,形成一类事物;然后用类去定义对象,形成这类事物的具体个体;类把属性和方法进行封装,同时对类的属性和方法进行访问控制。
下面举例圆类说明:
1)代码段:
//要使用C++输入输出工具,要提供这两行代码
#include //包含输入输出函数的头文件
using namespace std; //释放std命名空间中的变量名,函数名以及类型名
//声明常量PI=3.14
const double PI=3.14;
class Circle
{
public: //访问权限:公共的权限
//属性
double R;//半径
//方法
//获取圆的周长
double cal_zc()
{
return 2*PI*R;
}
//获取圆的面积
double cal_mj()
{
return PI*R*R;
}
};
int main()
{
//创建对象
Circle c; //声明类的对象
c.R=10; //创建对象
cout<<"半径为:"<
2)结果:
3)解释代码:
①Circle是一个类,其中封装了一些方法:
一个属性:R
两个方法:cal_zc()和cal_mj(),分别计算圆的周长和面积
②public规定了类中的内容的访问权限。(访问权限有public,private和protected)
public:公有成员,可以在任何地方访问。
private:私有成员,只能在类内部访问。
protected:保护成员,可以在类内部或子类内部访问。
③main函数:
int main()
{
//创建对象
Circle c; //声明类的对象
c.R=10; //创建对象
cout<<"半径为:"<
对象是根据类来创建的,声明类的对象就像声明基本类型的变量一样。
Circle类的公共成员可以直接被对象用''访问。
c.R访问了公共成员属性半径。
c.cal_zc和c.cal_mj访问了两个公共成员函数,分别计算了周长和面积。
(3)类包含的四个要素:
classname
{
access specifiers//访问修饰符:public/private/protected
date members/variables;//变量
member fuctions(){}//方法
};//分号结束一个类
1)类名classname
2)访问修饰符access specifiers
public:公有成员,可以在任何地方访问。
private:私有成员,只能在类内部访问。
protected:保护成员,可以在类内部或子类内部访问。
3)属性
4)方法
成员函数也可以类内声明,类外实现。注意,在类外实现时需要加上Circle::来指定作用域,表示实现的是Circle类中的成员函数。
class Circle
{
public: //访问权限:公共的权限
//属性
double R;//半径
//方法
//获取圆的周长
double cal_zc();
//获取圆的面积
double cal_mj();
};
double Circle::cal_zc()
{
return 2*PI*R;
}
double Circle::cal_mj()
{
return PI*R*R;
}
(4)class与struct的区别:
class默认权限为私有private
struct默认权限为公共public
struct A
{
int iNum;
}
class B
{
int iNum;
}
A a;
a.iNum=2;//没有问题,struct默认权限为public
B b;
b.iNum=2;//编译出错,class默认权限为private
2.创建对象:
(1)对象的初始化和清理:
对象的初始化和清理是两个非常重要的安全问题。一个对象或者变量没有初始化状态,对其使用后果是未知的。使用完一个对象或变量,没有及时清理也会造成一定的安全问题。
C++利用构造函数和析构函数解决上述问题,这两个函数会被编译器自动调用。
(2)构造函数:
构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用。
1)构造函数的结构:
class Person
{
public:
//有参构造函数
Person(string name, int age)
{
this->name=name;
this->age=age;
}
//this是一个指向当前类的指针
//拷贝构造函数
Person(const Person& p)
{
this->name=p.name;
this->age=p.age;
}
//拷贝构造中传入的参数是以引用的方式传入,可节省空间
//const是为了确保不改变传入的参数p
public:
string name;
int age;
};
该Person类中有两个属性name和age,利用函数重载机制编写了有参构造和拷贝构造两种构造函数,编译器会根据调用时参数的不同,自动选择正确的构造函数。在不提供构造函数时,编译器会自动提供一个默认的无参构造函数。
2)构造函数的调用方法:
#include
#include
using namespace std;
class Person
{
public:
//有参构造函数
Person(string name, int age)
{
this->name = name;
this->age = age;
}
//拷贝构造函数
Person(const Person& p)
{
this->name = p.name;
this->age = p.age;
}
public:
string name;
int age;
};
void test01()
{
Person p1("Tom",10);//括号法
Person p2 = Person("John",20);//显式构造法
Person p3 = { "Harry",30 };//隐式构造法
Person p4(p2);//拷贝构造
cout << p1.name << " " << p1.age << endl;
cout << p2.name << " " << p2.age << endl;
cout << p3.name << " " << p3.age << endl;
cout << p4.name << " " << p4.age << endl;
}
int main()
{
test01();
return 0;
}
有参构造函数有三个调用方法:括号法,显式法,隐式法。
拷贝构造则是在创建对象p4的时候直接传入一个已有的Person对象p2,利用p2的属性初始化p4。
(3)析构函数:
析构函数主要作用在于对象销毁前系统自动调用,执行一些清理工作。析构函数在对象消亡时自动调用。析构函数名与类名相同,前面加~,没有参数和返回值。
#include
using namespace std;
class Person
{
public:
//有参构造函数
Person(string name, int age)
{
this->name=name;
this->age=age;
cout<<"构造函数调用"<
运行结果显示了一个对象从创建到消亡的过程。在“析构函数调用”后打印输出“1111”,说明在执行完函数test02()时析构函数立即被调用,代表在函数中创建的对象被销毁。
3.静态成员:
(1)定义:
在成员变量和成员函数前加上关键字static称为静态成员。静态成员分为静态成员变量和静态成员函数。
1)静态成员变量:
所有对象共享同一份数据;在编译阶段分配内存;类内声明,类外初始化。
2)静态成员函数:
所有对象共享同一个函数;静态成员函数只能访问静态成员变量。
class Runoob
{
public:
static int runoob_age;//静态变量
int runoob_users;//实例变量
public:
func
{
int runoob_local;//局部变量
};
};
(2)静态成员访问:
访问方式有两种:对象.或者类名::
#include
using namespace std;
class Person
{
public:
static int count;//静态成员变量
string name;
};
int Person::count=10;//类外初始化
void test01()
{
Person p1;
p1.name="Tom";
cout<<"p1的名字:"<
count是Person类中的一个静态成员变量,可以用p1.count和Person::count两种方式来访问。而name是Person类中的一个普通成员变量,只能用p1.name的方式访问。
运行结果如下:
(3)示例:创建了两个对象,都对count属性进行了改变
#include
using namespace std;
class Person
{
public:
static int count;//静态成员变量
string name;
};
int Person::count=10;//类外初始化
void test01()
{
Person p1;
p1.count=0;
cout<<"count:"<
运行结果如下:
运行结果说明Person类的所有对象共享同一个变量count。
4.访问权限:
(1)数据封装:
数据封装是面向对象编程的一个重要特点,它防止函数直接访问类的内部成员。类成员的访问限制是通过在类主体内部对各个区域标记public、private、protected来指定的。public、private、protected称为访问修饰符。
一个类可以有多个访问修饰符标记区域,类默认的访问修饰符是ptivate。
(2)访问修饰符:
1)public:公有成员,可以在任何地方访问。
2)private:私有成员,只能在类中或者友元函数中访问。
3)protected:保护成员,可以在类中的函数、子类函数及友元函数中访问。
分析示例:
#include
using namespace std;
class Person
{
//姓名,公共权限
public:
string m_Name;
//汽车,保护权限
protected:
string m_Car;
//银行卡密码,保护权限
private:
int m_Password;
public:
void func()
{
m_Name="张三";
m_Car="拖拉机";
m_Password=123456;
cout<
输出结果如下:
在这个例子中可以看出,func()作为成员函数可以访问私有成员变量和保护成员变量,然而在类外函数test01()中就不能访问m_Car和m_Password。
(3)友元:
类的友元函数是定义在类外部,但有权访问类的所有私有成员和保护成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数(友元函数),也可以是一个类(友元类),在这种情况下,整个类及其所有成员都是友元。
示例:
#include
using namespace std;
class Box
{
double width;
public:
friend void printWidth(Box box);
void setWidth(double wid);
};
//成员函数定义
void Box::setWidth(double wid)
{
width=wid;
}
//请注意:printWidth()不是任何类的成员函数
void printWidth(Box box)
{
/*因为printWidth()是Box的友元,它可以直接访问该类的任何成员*/
cout<<"Width of box:"<
输出结果: