C++基本语法

文章目录

前言

目前正计划学习QT开发,但是之前一直都是做MCU开发和linux驱动开发,用的都是C语言开发的,对C++一点概念都没有,因此笔者将对自己近期C++的学习情况进行记录。

输入输出

在C语言中,我们输入输出如下所示:
在 C++里,我们使用以 cin 和 cout 代替了 scanf 和 printf。在输入和输出的流程上是不变的, 只是关键字变了,用法也变了。

cout 语法形式如下,x 可以是任意数据类型,甚至可以写成一个表达式,endl 指的是换行符,与 C 语言的"\n"效果一样。

c 复制代码
cout<<x<<endl;
cin>>x;

在变量间不能用",",如果需要两个变量需要用下面这种方式实现:

c 复制代码
cout<<x<<y;
cin>>x>>y;

命名空间

c 复制代码
#include <iostream> 
using namespace std;
int main() {
	cout << "Hello, World!" << endl;
	return 0;
}

< iostream > 不能写成 iostream.h,有.h 的是非标准的输入输出流,c 的标准库。无.h 的是标准输入输出流就要用命名空间。using 是编译指令,声明当前命名空间的关键词。因为 cin 和 cout 都是属于 std 命名空间下的东西, 所以使用时必须加上 using namespace std;这句话。cin 和 cout 可以写 std::cin 和 std::cout," ::" 表示作用域,cin 和 cout 是属于 std 命名空间下的东西,这里可以理解成 std 的 cin 和 std 的 cout。

为啥要用命名空间呢? 有些名字容易冲突,所以会使用命名空间的方式进行区分,具体来说就是加个前缀。比如 C++ 标准库里面定义了 vector 容器,您自己也写了个 vector 类,这样名字就冲突了。于是标准库里的名字都加上 std:: 的前缀,您必须用 std::vector 来引用。同理,您自己的类也可以加 个自定义的前缀。但是经常写全名会很繁琐,所以在没有冲突的情况下您可以偷懒,写一句 using namespace std;,接下去的代码就可以不用写前缀直接写 vector 了。

前面所说的std是标准库的命名空间,下面举一个自己创建的命名空间的例子:

c 复制代码
namespace A {
	int x = 1; 
	void fun() {
		cout<<"A namespace"<<endl;
	}
}
using namespace A;
int main() {
	fun();
	x = 3; 
	cout<<x<<endl;
	A::x = 3; 
	cout<<A::x<<endl;
	A::fun();
	return 0;
}

如上所示,我们自己定义了一个命名空间A,在A中定义了 fun 函数和 x 变量,当且仅当我们定义了using namespace A;这句话时,我们才可以直接用man函数的前三句话实现效果,如果没有这句话时,我们只能用man函数的后三句用 A:: 来指定命名空间来实现效果。

类和对象

接下来就是C++中十分重要的类和对象了,类是 C++ 的核心特性,通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。函数在一个类中被称为类的成员。

下面笔者将从类中实例化对象进行讲解类和对象,这里通过两种方法来实例化对象,一种是从栈中实例化对象,一种是从堆中实例化对象。

c 复制代码
#include <iostream>
#include <string>
using namespace std;
class Dog
{
public:
	string name;
	int age;
	void run() {
		cout<<"小狗的名字是:"<<name<<","<<"年龄是"<<age<<endl;
	}
};

int main()
{
	Dog dog1;
	dog1.name = "旺财";
	dog1.age = 2;
	dog1.run();
	Dog *dog2 = new Dog();
	if (NULL == dog2) {
		return 0;
	}
	dog2->name = "富贵";
	dog2->age = 1;
	dog2->run();
	delete dog2;
	dog2 = NULL;
    return 0;
}

如上所示,先用class创建一个Dog的类,在这个类中笔者创建了一些变量和函数成员,在 mian 函数中,先是直接在栈中创建了一个dog1的对象,随后便可用dog1这个对象来设置对应的成员了,同理,也可以通过关键字 new 从堆中实例化对象,dog2对象是一个指针,指向开辟的内存,通过dog2也可以实现对各个成员的访问。

构造函数、析构函数

构造函数在对象实例化时被系统自动调用,仅且调用一次。与构造函数相反,析构函数在对象结束其生命周期时系统自动执行。实际上定义类时,如果没有定义构造函数和析构函数,编译器就会生成一个构造函数和析构函数,只是这个构造和析构函数什么事情也不做,所以我们不会注意到一点。

构造函数的特点如下:(1) 构造函数必须与类名同名; (2) 可以重载; (3) 没有返回类型,即使是 void 也不行。

析构函数的特点如下: (1) 析构函数的格式为~类名(); (2) 调用时释放内存(资源); (3) ~类名()不能加参数; (4) 没有返回值,即使是 void 也不行。

如下所示,为构造函数和析构函数的一个具体实例

c 复制代码
class Dog{
public:
	Dog();
	~Dog();
};
int main(){
	Dog dog;
	cout<<"构造与析构函数示例"<<endl;
    return 0;
}
Dog::Dog(){
	cout<<"构造函数执行!"<<endl;
}
Dog::~Dog(){
	cout<<"析构函数执行!"<<endl;
}

this指针

一个类中的不同对象在调用自己的成员函数时,其实它们调用的是同一段函数代码,那么 成员函数如何知道要访问哪个对象的数据成员呢?每个对象都拥有一个 this 指针,this 指针记录对象的内存地址。 在 C++中,this 指针是指向类自身数据的指针,简单的来说就是指向当前类的当前实例对象,this 只能在成员函数中使用。

如下所示,是利用 this 指针的一个实例:

c 复制代码
class Dog{
public:
	string name;
	void func();
};
int main(){
	Dog dog;
	dog.func();
    return 0;
}
void Dog::func(){
	this->name = "旺财";
	cout<<"小狗的名字叫:"<<this->name<<endl;
}

继承

继承允许我们依据另一个类来定义一个类, 这使得创建和维护一个应用程序变得更容易,重用代码功能和提高执行效率的效果。

当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。派生类的继承方式有以下三种:

  1. 公有继承(public):当一个类派生继承公有基类时,基类的公有成员也是派生类的公有成 员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但 是可以通过调用基类的公有和保护成员来访问。
  2. 保护继承(protected): 当一个类派生继承保护基类时,基类的公有和保护成员将成为派 生类的保护成员。
  3. 私有继承(private):当一个类派生继承私有基类时,基类的公有和保护成员将成为派生类 的私有成员。

下面以公有继承为例展示一段应用代码

c 复制代码
class Animal {
	public: 
	/* 颜色成员变量 */
	string color; 
	/* 体重成员变量 */ 
	int weight;
};
class Dog : public Animal  //这一句话就是类的共有继承,把public改成protected 或 private就变成其他继承方式。
{
public: 
	string name;
	int age; 
	void run();
};

此时,我们通过Dog的类创建一个dog1对象,dog1不仅可以访问自身的 name,age,run,还可以访问基类的 color 和 weighht。

重载

允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符 重载。

函数重载

在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。程序便可以通过形参的不同或者返回值的不同自主的选择合适的函数执行。下面是一个例子方便读者理解。

c 复制代码
class Dog {
public:
	string name; 
	void getWeight(int weight) { 
		cout<<name<<"的体重是:"<<weight<<"kG"<<endl;
	}
	void getWeight(double weight) {
		cout<<name<<"的体重是:"<<weight<<"kG"<<endl;
	}
};

int main() {
	Dog dog;
	dog.name = "旺财"; 
	dog.getWeight(10);
	dog.getWeight(10.5);
	return 0;
}

如上所示,当我们给 getWeight 函数传入的是整形的时候他会自动匹配第一个函数执行,传入浮点数时会自动匹配第二个函数执行。有可能用户传入的参数类型不在我们写的重载函数里,假若用户传入了一个字符串类型,这样编译器就 会匹配不到相应的重载函数,编译时就会报错。

运算符重载

运算符重载相比函数重载要复杂一点,并且用得相对少一点。重载运算符的格式如下:

c 复制代码
<返回类型说明符> operator <运算符符号>(<参数表>)
{
	<函数体> 
}


接下来以以重载"+"运算符为例使用成员函数演示运算符重载的概念:

c 复制代码
class Dog {
public: 
	int weight; 
	Dog operator+(const Dog &d) {
		Dog dog;
		dog.weight = this->weight + d.weight;
		return dog;
	}
};
int main() {
	Dog dog1; 
	Dog dog2; 
	Dog dog3;
	dog1.weight = 10;
	dog2.weight = 20;
	dog3 = dog1 + dog2; 
	cout<<"第三只狗的体重是:"<<dog3.weight<<endl; 
	return 0;
}

重载运算符"+",可以把两个对象进行相加。在普通的算术运算符"+" 是不能将两个对象进行相加的,所以我们重载运算符的意义可以体现在这里。

多态

形成多态必须具备三个条件:1. 必须存在继承关系;2. 继承关系必须有同名虚函数;3. 存在基类类型的指针或者引用,通过该指针或引用调用虚函数。

虚函数是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时, 会告诉编译器不要静态链接到该函数。

纯虚函数: 若在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。纯虚函数声明如下: virtual void funtion1()=0; 纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。 包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用。

下面是一个虚函数构成多态的一个实例:

c 复制代码
class Animal{/* 定义一个动物类 */
public:
	virtual void run() {
		cout<<"Animal的run()方法"<<endl;
	}
};
class Dog : public Animal{/* 定义一个狗类,并继承动物类 */
public:
	void run() {
		cout<<"Dog的run()方法"<<endl;
	}
};
class Cat : public Animal{/* 定义一个猫类,并继承动物类 */
public:
	void run() {
		cout<<"Cat的run()方法"<<endl;
	}
};
int main(){
	Animal *animal;/* 声明一个Animal的指针对象,注:并没有实例化 */
	Dog dog;/* 实例化dog对象 */
	Cat cat;/* 实例化cat对象 */
	animal = &dog;/* 存储dog对象的地址 */
	animal->run();/* 调用run()方法 */
	animal = &cat;/* 存储cat对象的地址 */
	animal->run();/* 调用run()方法 */
	return 0;
}

如上所示,在基类中用 virtual 定义了一个虚函数run,当基类的 run()方法定义成虚函 数,编译器不静态链接到该函数,它将链接到派生类的 run()方法,进行实例化。在派生类dog和cat中也存在函数run,当把dog和cat的对象指针指向基类时,运行的是对象的run函数,而不是基类的run函数。

数据封装

封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念,这样能避免受 到外界的干扰和误用,从而确保了安全。

通常情况下,我们都会设置类成员状态为私有(private),除非我们真的需要将其暴露,这样才能保证良好的封装性。下面我们还是以狗类为例,增加一个食物的方法 addFood(int number)。将获得食物的方法设定在 public 下,这样 addFood(int number)方法就暴露出来了,也就是对外的接口。然后我们设置狗类的私有成员(private)食物的份数 total。total 为获得的食物总数,然后 我们还写一个公开的方法 getFood()在 public 下,通过 getFood()来打印出小狗总共获得了几份食物,通过此方法,可以很好的保护 total 数据的安全。该案例的具体实现如下:

c 复制代码
class Dog{
public:
	string name;
	Dog(int i = 0){
		total = i;
	}
	void addFood(int number) {
		total = total + number;
	}
	int getFood() {
		return total;
	}
private:
	int total;
};
int main()
{
	Dog dog;
	dog.name = "旺财";
	dog.addFood(3);
	dog.addFood(2);
	cout<<dog.name<<"总共获得了"<<dog.getFood()<<"份食物"<<endl;
    return 0;
}

这里需要注意的是,Dog 类中的 Dog 函数是属于构造函数,用于初始化 total 变量的,且变量的初始化尽量不要在类中直接定义,有的编译器可能不行。通过上面这个程序我们可以通过两个函数去改变total的值。

数据抽象

数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。

数据抽象有以下好处:1. 类的内部受到保护,不会因无意的用户级错误导致对象状态受损。 2. 类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告。

像 cout 这个对象就是一个公共的接口,我们不必要知道 cout 是如何在屏幕上显示内容 的。cout 已经在底层实现好了。

接口(抽象类)

抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。如 果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使 用 "= 0" 来指定的。

设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。 抽象类不能被用于实例化对象,它只能作为接口使用。如果试图实例化一个抽象类的对象,会 导致编译错误。如果一个 ABC 的子类需要被实例化,则必须实现每个虚函数。

抽象类与前面所提到的多态类似,与之不同的是之前Aninmal 类的 run()方法定义为虚函数,这里Aninmal 类的 run()方法定义为纯虚函数,多态需要通过基类指针实现,抽象类不需要。下面是抽象类的一个实现例程:

c 复制代码
class Animal{ /* 定义一个动物类 */
public:
    virtual void run() = 0;
};
class Dog : public Animal{ /* 定义一个狗类,并继承动物类 */
public:
    void run() {
        cout<<"Dog的run()方法"<<endl;
    }
};
class Cat : public Animal{/* 定义一个猫类,并继承动物类 */
public:
    void run() {
        cout<<"Cat的run()方法"<<endl;
    }
};
int main(){
    Dog dog; /* 实例化dog对象 */
    Cat cat;/* 实例化cat对象 */
    dog.run();/*	dog调用run()方法 */
    cat.run();/* cat调用run()方法 */
    return 0;
}

后续

由于目前主要需求是QT的开发,而不是C++本身,因此目前就掌握了相关的基本概念,后续如需深入学习会继续在本专栏记录。

相关推荐
我是唐青枫2 分钟前
C# delegate 委托使用教程
开发语言·c#·.net
李老头探索14 分钟前
深入解析 JVM vs JDK vs JRE:三者区别与联系详解
java·开发语言·jvm
懒大王爱吃狼33 分钟前
python基于diagrams库绘制系统架构图
开发语言·python·系统架构·自动化·python基础·python教程
JavaPub-rodert33 分钟前
项目48:简易语言学习助手【源代码】 --- 《跟着小王学Python·新手》
服务器·开发语言·python·学习·microsoft
吃不饱的得可可34 分钟前
【Qt】主窗口
开发语言·qt
_周游41 分钟前
【C语言】_指针运算
c语言·开发语言
浮生如梦_1 小时前
C#Halcon交互绘制ROI
开发语言·图像处理·计算机视觉·c#·视觉检测·交互
慢慢_飞1 小时前
java.lang.Error: FFmpegKit failed to start on brand:
java·开发语言
CodeClimb1 小时前
【华为OD-E卷 - 最优资源分配 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
nfenghklibra1 小时前
python字符串函数用法大全
开发语言·python