我们这里分享的内容的都是基于有c基础的情况下
了解
1.C++是一种计算机高级程序设计语言,由C语言扩展升级而产生 ,最早于1979年由本贾尼·斯特劳斯特卢普在AT&T贝尔工作室研发。
2.C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计。
3.C++拥有计算机运行的实用性特征,同时还致力于提高大规模程序的编程质量与程序设计语言的问题描述能力。
课程重要性
1.c++是面向对象语言,面向对象语言是主流的编程思想 --跟上时代步伐
2.Qt是嵌入式方向图形化开发。C++是它的前置课程
3.C++和Qt相对独立的体系。
4.C++和Qt是一个工作岗位的机会
C++在嵌入式中可以用于:
系统开发,算法开发,图形用户界面(GUI)开
C++的特点
1.c语言基础上 ,支持面向对象开发
2.功能强大,应用领域广泛
3.为数不多可以底层开发的面向对象语言。开发系统软件
4.在面向对象语言重开发效率高
C++的编译器选择
这里我们用的是
C++的基本语法
C++程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要的看一下什么是类,对象,方法,即使变量。
对象 : 对象具有状态和行为。例如:一只狗的状态: (颜色、名称、品种) ,行为:( 摇动、叫唤、吃。)对象是类的实例。
类 : 类可以定义为描述对象的集合。(人类:行为:吃喝玩乐,状态:男女老少)
方法 : 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。
即时变量 : 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。
程序结构
在c语言中的程序结构
#include<stdio.h> //头文件
int main() //主程序,程序执行的地方
{
}
而在c++中也有固定的程序结构
#include <iostream>
using namespace std;
// main() 是程序开始执行的地方
int main()
{
cout << "Hello World"; //这里的cout相当于c语言中的printf
return 0;
}
接下来我们讲解一下上面这段程序:
第一行:#include iostream C++ 语言定义了一些头文件,这些头文件包含了程序中必需的或有用的信息。
第二行: using namespace std; 的翻译为使用命名空间std。命名空间作为c++中一个相对新的概念,命名空间(名字空间)为避免多个程序员共同开发时使用大量的变量和函数名,出现命名冲突。实际意义就是将自己的定义的标识符(用来标识变量、函数、类、模块,或任何其他用户自定义项目的名称)放在自己的命名空间中,这样就不会出现命名冲突相对独立。http://c.biancheng.net/view/2192.html详解。
后面则是主函数。
引用
本质:C语言中的指针常量,引用就是给一个变量取一个别名,操作引用就相当于操作变量本身。
#include <iostream>
using namespace std;
int main()
{
int jiNan=90;
int &quanCheng=jiNan;
quanCheng+=5;
//cout是输出 endl:end line 换行
cout<<quanCheng<<" "<<jiNan<<endl; //95 95
cout<<&quanCheng<<" "<<&jiNan<<endl; //0x61fe88 0x61fe88
}
引用的性质
1.当成为一个变量的引用之后,就不能成为其他变量的引用。
int jiNan=90;
int qingDao=91;
int &quanCheng=jiNan;
//&quanCheng=qingDao;//错误 不能重新赋值
2.引用 必须有初始值,并且不能为NULL。
int jiNan=90;
int qingDao=91;
//int &quanCheng; //错误必须赋值
//int &quanCheng=NULL;//不能赋值NULL
3.如果初始化的内容是纯数字,引用需要定义为常引用。代表引用不可以更改所指向的内容。
//int &b=100; 错误
const int & b=100;
cout<<b<<endl;
4.如果引用定义成常引用。不能直接更改变量的值,但是通过变量可以间接改变引用的值
int a=100;
const int & b=a;
cout<<a<<" "<<b<<endl; //100 100
//b++; error: increment of read-only reference 'b'
a++;
cout<<a<<" "<<b<<endl; //101 101
引用的应用
引用作为参数,不需要给参数开辟空间,也没有产生副本,提高了效率。
#include <iostream>
using namespace std;
//相当于C语言中的值传递,没有改变值。
void test(int a,int b){
int t=a;
a=b;
b=t;
}
//相当于C语言中的地址传递,改变了变量的值
void test2(int * a,int * b){
int t=*a;
*a=*b;
*b=t;
}
//引用传参,比地址传递更加简介,提高啦效率
void test3(int & n,int & m){
int t;
t=n;
n=m;
m=t;
}
int main()
{
int n=10;
int m=20;
test(n,m);
cout<<n<<" "<<m<<endl; //10 20
test2(&n,&m);
cout<<n<<" "<<m<<endl; //20 10
test3(n,m);
cout<<n<<" "<<m<<endl; //10 20
}
赋值
在C语言中我们的赋值和强转是:
int a=100;
double b=(int)a;
而在c++中更加简便:
int a=100; //也可以使用
int b(200); //c++赋值
cout<<a<<" "<<b<<endl; //100 200
double c=3.14;
int d(c); //强制转化
cout<<d<<endl; //3
在C语言中的从终端输入是:scanf
而c++中键盘输入为:cin
cin输入 ,cout输出
iostream中 i代表input o代表outpu
#include <iostream>
using namespace std;
int main()
{
int a;
cin>>a;
cout<<"输入的值是"<<a;
}
string类型 字符串
在c++中字符串是一个类,内部有 很多的方法,方便处理字符串内容,而在C语言中字符串是一个数据类型,通常用于字符串常量,存储于字符数组当中。
#include <iostream>
using namespace std;
int main()
{
string s="123456";
cout<<s[0]<<endl;
cout<<s[6]<<endl; //效率高,越界不报错
//cout<<s.at(6)<<endl;//更安全 会报超出范围错误
cout<<s<<endl;
cout<<"字符串长度"<<s.length()<<endl; //6
//for循环遍历
for(int i=0;i<s.length();i++){
cout<<s[i]<<" ";
}
cout<<endl;
//for each方式遍历
cout<<"用for each方式取"<<endl;
//字符串中每个元素是char类型的。
//string 对应字符数组 字符数组每一个元素是char
for(char c:s){
cout<<c<<" ";
}
}
内联函数
作用:
1.提高函数调用效率。当函数仅有简单几行代码时,调用此函数的主要开销在调用机制上了,可能还要别的文件查询,而内联函数就节省了这一笔开销。但是需要注意的是,仅在函数功能十分简单的时候才有用。
2.代替带参数的宏。
具体的作用和意义可参考:https://blog.csdn.net/qq_35902025/article/details/127912415
用法:
在定义上加上inline关键字,在编译时自动展开到主函数中。
#include <iostream>
using namespace std;
void show(); //声明
inline void show(){ //定义
cout<<"内联函数";
}
int main()
{
show();
}
函数重载
解释:函数可以重名,根据参数的个数,参数的类型不同来区分具体调用那个函数,不能以返回值来区分。
存在的作用:
1.减少对用户的复杂性,省去解决相似问题,取名字花费时间,提高效率。
2.减少了函数名的数量,避免了名字空间的污染,有利于程序的可读性。
示例:
1.根据参数个数不同选取不同函数
void salary(int jiBen){
cout<<"文员工资"<<jiBen<<endl;
}
void salary(int jiBen,int jiangJin){
int total=jiBen+jiangJin;
cout<<"销售工资"<<total;
}
2.根据参数类型不同选取不同函数
void show(int a){
cout<<"int:"<<a<<endl;
}
void show(string s){
cout<<"string:"<<s<<endl;
}
函数的默认值
1.当有参数默认值的函数被调用的时候,如果不传参,就会选取默认值,否则就改成传入的数据。
#include <iostream>
using namespace std;
void draw(string color="黑色"){
cout<<color<<endl;
}
int main()
{
draw();
draw("红色");
}
2.声明和定义只能一个位置加默认值。
#include <iostream>
using namespace std;
void method(int a=10); //声明处加默认值
void method(int a){
cout<<a<<endl;
}
int main()
{
method();
}
3.当有多个参数加默认值时,”向后原则”,中间有一个参数有默认值,其后的参数都需要加默认值。
#include <iostream>
using namespace std;
void method(int a=10); //声明处加默认值
void method(int a){
cout<<a<<endl;
}
//定义加默认值 b加了默认值 c也必须加默认值
void method2(int a,int b=20,int c=40){
cout<<a<<" "<<b<<" "<<c;
}
int main()
{
method2(10,30);
}
4.函数重载不要和默认值一块用 可能会产生二义性,产生错误,编译器无法做出选择。
#include <iostream>
using namespace std;
void method(int a){
cout<<a<<endl;
}
void method(int a,int b=10){
cout<<a<<endl;
cout<<b<<endl;
}
int main()
{
//method(20); 错误
}
补充:
二义性:指的是一个东西在一种环境下会出现两种以上(包含两种)含义,导致难以清楚到底何种意思。
哑元函数
解释:参数只有类型,但是没有名字。这个参数就成为哑元函数。
意义:保持向前的兼容性,比方说我们需要做成一个成品,然后成品是会不断的更新第一代第二代,当我们改进内容的时候新用户和老用户可能会有冲突,我们这个时候就利用哑元函数来保持兼容性。
用法:
#include <iostream>
using namespace std;
void method(int){
cout<<"hello"<<endl;
}
int main()
{
method(20);
}
面向对象基础
面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存的整体–对象。对同类对象抽象出其共性,形成类。类中的大多数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。
类与对象的概念
人可以作为一个对象,一个对象包括属性和行为
人的属性:姓名,性别,年龄 。 又称成员变量
人的行为:吃饭,跑步,学习。 又称成员函数/成员方法
属性:都是名词,用来描述事物是什么样子的
行为:都是动词。用来描述事物能做什么
类就是把对象的属性和行为“捆绑”在一起,作为一个整体。
一个车类:
class Phone{ //类首字母大写
public: //公共权限
string band;
string model;
int price;
//打电话
void communicate(){
cout<<"打电话"<<endl;
}
//播放音乐
void play_music(){
cout<<"虽然我只是一只羊~"<<endl;
}
//玩游戏
void play_game(){
cout<<"Timi"<<endl;
}
};
实例化对象
1.栈内存创建对象
类名+对象名
执行完毕自动销毁。
调用方式通过:对象名.调用函数
Phone p;
p.brand="iphone";
2.堆内存创建对象
用new关键字创建。delete销毁对象,如果不销毁,会造成内存垃圾。运行卡慢。
delete指向对象的指针
Phone * p2=new Phone;
p2->brand="xiaomo";
delete p2;
p2=NULL;
封装
把属性和行为作为一个整体表现生活中的事务
对属性和行为加以权限限制。如果不给定权限,默认为private(私有)。
访问权限有三种:
1.public 公共权限
公共权限,类内可以访问,类外也可以访问
2.protected 保护权限
保护权限,类内可以访问,类外不可以访问,子可以访问父中的保护内容
3.private 私有权限
私有权限,类内可以访问,类外不可以访问,子不可以访问父中私有内容
举例:
#include <iostream>
using namespace std;
class Person{
private:
string name; //可读可写
string address; //只读
string password="123456"; //只写
public:
void set_name(string n){
name=n;
}
void get_name(){
cout<<name;
}
void get_address(){
cout<<"你可以去休息了"<<endl;
}
void set_password(string p){
password=p;
}
void show(){
cout<<name<<" "<<password<<endl;
}
};
int main()
{
Person p;
//string s="hello";
//p.name=s;错误 私有成员不能直接访问
p.set_name("张三");
p.show();
p.get_address();
}
构造函数
用以初始化对象的数据成员的一种函数。
构造函数的规则:
1.构造函数与类同名且无返回值,在对象实例化时自动调用。
2.构造函数可以有多个重载形式。
3.实例化对象时仅用到一个构造函数。
4.当用户没有定义构造函数时,编译器自动生产一个构造函数。
class School
{
public:
School() // 类School的构造函数
{
m_strName = “bullworth”;
}
private:
string m_strName; // 数据成员
}
/* 该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作。*/
构造函数的种类
1.无参构造函数:
class Student
{
public:
Student()
{
m_strName= "luis";
m_iAge= 18;
}
private:
string m_strName;
int m_iAge;
}
1)如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做。
2)只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来。
2.一般构造函数(重载构造函数):
Student(string _name, int _age)
{
m_strName=_name; //将参数_name赋值给数据成员
m_iAge =_age; //将参数_age赋值给数据成员m_iAge
}
一般构造函数可以由各种参数形式,一个类可以有多个一般构造函数,函数名相同,和重载的原理相同。
注意:当你只定义了一个有参的构造函数时,通过实例化对象调用的时候,需要给其填充参数。否则编译器会报错。也可以给构造函数全部的参数给默认值。
初始化列表:
#include <iostream>
using namespace std;
class Person{
private:
string name;
string address;
string password="123456";
public:
Person(){
name="张三";
address="华清远见";
password="88888";
}
Person(string n,string a,string p): name(n),address(a),password(p){}
void show(){
cout<<name<<" "<<password<<" "<<address<<endl;
}
};
int main()
{
Person p;
p.show();
Person p2("李四","济南","12345");
p2.show();
}
析构函数
作用:
析构函数与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作。
1)对象销毁时会自动调用,没有参数和返回值 也就不能重载。
1)栈内存对象 编译器自动销毁 自动调用析构。
3)堆内存对象 delete之后 自动调用析构函数。
4)析构函数在对象销毁 做善后工作,回收new方式创建的资源。
用法:
就是将无参构造函数的前面加~
~Cat(){
cout<<name<<"析构函数"<<endl;
堆与栈析构的区别
#include <iostream>
using namespace std;
class Cat{
private:
string name;
public:
Cat(string n):name(n){
cout<<name<<"的构造函数调用"<<endl;
}
~Cat(){
cout<<name<<"的析构函数"<<endl;
}
};
int main()
{
Cat c("小花");
Cat * c2=new Cat("小白"); //堆内存对象需要 手动delete
//delete c2;
}
栈:执行对象完毕后,直接调用析构函数。
堆:执行对象完毕后,需要释放对象资源,释放后调用析构函数。
作用域限定符
理解:
:: //作用域限定符
using namespace std
std是c++中的标准命名空间,里面包含了很多内容。
#include <iostream.h>
int amount=123; //全局变量
void main()
{
int amount=456; //局部变量
cout <<::amount << ' ,'; //输出全局变量
cout <<amount << ' ,'; //输出局部变量
::amout=789;
cout <<::amount << ' ,'; //输出全局变量
cout <<amount << '\n'; //输出局部变量
}
运行结果为:
123,456,789,456
作用域限定符应用
类成员在类内做生命,类外定义时,类外定义需要加上作用域限定符。
#include <iostream>
using namespace std;
class Student{
private:
string name;
public:
Student(string n);
void show(); //类内声明
};
void Student::show(){ //类外定义
cout<<name;
}
//注意构造函数没有返回值
Student::Student(string n):name(n){}
int main()
{
Student s("小明");
s.show();
}
explicit
作用: 屏蔽隐式调用构造函数,防止误操作。
#include <iostream>
using namespace std;
class Student{
private:
string name;
public:
explicit Student(string n);
void show(); //类内声明
};
void Student::show(){ //类外定义
cout<<name;
}
Student::Student(string n):name(n){}
int main()
{
Student s("小明");
s.show();
string str="小强";
// Student s2=str; //隐式调用构造函数 加explicit后不允许
// s2.show();
}
拷贝构造函数
下面三种对象需要调用拷贝构造函数:
- 一个对象作为函数参数,以值传递的方式传入函数体;
- 一个对象作为函数返回值,以值传递的方式从函数返回;
- 一个对象用于给另外一个对象进行初始化(常称为赋值初始化);
正确拷贝:
正确拷贝的概念:
对象之间相互独立 对象之间的属性也是独立的。只是把一个对象的值赋给了另一个对象。
当类中的属性没有指针时,用到浅拷贝。
#include <iostream>
#include <string.h>
using namespace std;
class Phone{
private:
string brand;
string model;
public:
Phone(string b,string m);
void show();
Phone(const Phone & p){ //**不写默认会有**
cout<<"拷贝构造函数"<<endl;
brand=p.brand;
model=p.model;
}
};
Phone::Phone(string b,string m){
brand=b;
model=m;
}
void Phone::show(){
cout<<brand<<" "<<model<<endl;
cout<<&brand<<" "<<&model<<endl;
}
int main()
{
Phone p("苹果","14");
p.show();
cout<<"------------"<<endl;
Phone p2(p);
p2.show();
}
如果是属性中的指针类型,地址也会拷贝过去。破坏对象之间的独立性,就需要每个对象创建一个属于自己的空间
当类中的属性有指针时,用到深拷贝
#include <iostream>
#include <string.h>
using namespace std;
class Dog{
private:
char * name;
public:
Dog(char * n){
name=new char[20];
strcpy(name,n);
}
void show(){
cout<<name<<endl;
}
Dog(const Dog & d){
name=new char[20];
strcpy(name,d.name);
}
};
int main()
{
char a[10]="xiaobai";
Dog d1(a);
Dog d2(d1);
d1.show(); //xiaobai
d2.show(); // //xiaobai
strcpy(a,"doudou");
d1.show(); //xiaobai
d2.show(); xiaobai
}
当属性中有指针时的错误拷贝,值传递
#include <iostream>
#include <string.h>
using namespace std;
class Dog{
private:
char * name;
public:
Dog(char * n){
name=n;
}
void show(){
cout<<name<<endl;
}
Dog(const Dog & d){
name=d.name;
}
};
int main()
{
char a[10]="xiaobai";
Dog d1(a); //d1.name指向a
Dog d2(d1); //d1.name也指向a
d1.show(); //xiaobai
d2.show(); //xiaobai
strcpy(a,"doudou");
d1.show(); //doudou
d2.show(); //doudou
}
值之间的传递。当属性有指针时出现问题 两个对象的成员变量保存同一个地址,指向同一片地址。
写不动啦!!!!