········自学教材:《C++ Primer Plus 中文第六版》
2.进入Cpp
2.1进入C++
Hello Wold!的C++版本
#include "iostream"
using namespace std;
int main(){
cout << "hello Wold!" << endl;
return 0;
}
C++的主函数
标准形式:
#include <iostream>//这里的 i 表示in ;o表示out;steam就是 流
using namespace std;
int main(int argc , char* argv[])
{
return 0;
}
C++的注释
相比较C:除了用 // 和 /……/,还可以利用if语言预处理命令进行注释:
#if 0
通常情况下0的意思就是“假”
随便写什么
多行注释
#endif
名称空间
使用cout进行C++的输出
cout是一个预定义的对象。
cout << "hello wold!" << ' ' << 12
<< ',' ; //可以连续输出 并且自动识别类型
endl 是 换行 关键字
并且 清空、刷新 缓冲区
当然,换行符 \n 也可以换行
缓冲区?:
输入的东西不会直接显示在屏幕上
内存中有缓冲区
它就是一个连续的空间
cin ,也是一个对象
输入 >> (符号前后的空格可加可不加)
输出 <<
int c;
cout << "请输入整数:" <<endl;
cin >> c;
cout << c << endl;
3.命名空间 namespace
功能:区别同名变量或者函数
(提供一种方法来区分)
创建:
(关键字 namespace)
namespace suibian
{
}//注意结尾没有分号“;”,和C里面的结构体区分开来,结构体花括号结尾有分号!
可以将同名但是功能不同的两个函数,其中一个,放进这个命名空间里面,这样就分开来。
比如上面说的 cout和cin 就都是装在
namespace std 里面的两个函数
使用方式:
方法一:
1⃣️打开命名空间:using namespace suibian
2⃣️然后紧跟着在下面调用函数名;
方法二:
调用形式:suibian+两个冒号+命名空间里面的函数成员名(记得后面的小括号要带上,有形参写参数?)
e.g.
suibian::sort();
//双冒号是 作用域运算符
所以说,如果我们在代码开头没有加上
using namespace std;
的话,后面我们使用输入输出函数的时候就需要:
std::cout<< "你学废了吗?"<< "hahaha";
<< std::endl;
方法三:(不常用)指定开放空间中某个特定成员
1⃣️ using std::cout;
2⃣️ 接下来使用cout就不需要用作用域运算符了,可以直接用名字了
数据类型
1.一些C的基本数据类型
2.构造类型:
数组
结构体
联合
枚举
其中结构体有C++的独有使用方法:
1.声明变量的时候可以不用struct关键字
2.可以放函数成员(C语言只能放函数地址)
3.是一个特殊的类
#include <iostream>
using namespace std;
struct Node
{
int m;
void fun()
{
printf("hello!");
}
}
int main()
{
Node a; //声明一个结构体
a.fun();
return 0;
}
3.指针
说到指针,我们就涉及到 堆区空间的申请和释放:
在C中,我们常用:malloc() 和 free()
Cpp中,我们用 : new 和 delete
区别?
使用 new 申请一块单个空间:
首先,申请一连串的连续空间的话,我们一定要有一个指针去保存它的地址:
int *p = (int*)malloc(sizeof(int));
//这里的(int*)是C中的强制类型转换
int *pp = new int;
/* new type 类型匹配非常重要!就是说,如果你申请这块空间是用来存储int类型的数据,那你就new int ; 如果你用来存储字符 ,那你就new char
*/
*pp = 12; //把12写入这块空间
cout << *pp << endl;//读取空间内容
//释放
delete pp; // delete + 指针名字
一个小tips:
int *pp = new int(10);
/*可以在申请空间的同时给这块空间初始化赋值,这里是10;注意,小括号内的类型要和前面匹配上
*/
使用new申请一个数组空间
int *pp = new int[5];
// 方括号里的 5 代表元素的个数
int *p = (int*)malloc(5*4);
//一个int 4个字节
pp[0] = 1;
pp[1] = 2;
cout << pp[0] << endl;
//最后不要忘记释放
delete[] p;
//注意数组空间需要在delete后面加上一对方括号
如何初始化申请的数组空间?
C++里面提供了一个函数:
int *p = new int[5];
memset(p,0,5*4);
//memory set :给数组里面所有的元素赋值0
cout << p[1]<<p[2]<<p[3]<<endl;
return 0;
4.C与Cpp申请空间的区别
首先,我们可以看到Cpp完全支持malloc和free函数;
对于
C与C++指针声明和空间分配方式不同,使用方式完全相同,所以,在Cpp里,除了对象空间申请之外,用malloc和free是完全可以的
malloc-free 与 new-delete 最大的区别在于:
new-delete 可以触发构造和析构;
构造和析构?
就是说,在申请对象空间的时候,需要用到new-delete
Cpp中 星号 * 的几种作用:
1⃣️用来声明指针变量
int *p = NULL;
2⃣️地址操作符(进行“读”“写”入)
int a=687;
int *p = &a;
cout << *p <<endl;
3⃣️乘法运算符
Note:
我们所写的代码,本质上都是对内存的操作:
读,写,取地址&;
4.引用变量
Cpp中特有的数据类型
(type) &+name 例如: int &c
概念:引用 是给已知变量(已定义变量)起个 别名。
(注意区分!C中的 typedef 是给 类型 起别名)
基本数据类型的引用:
//给变量起个别名
int a = 687;
int &c = a;//声明变量a的一个引用 c;
//c是变量a的一个别名,c和a用法完全一样
//&c 和 &a 的值也是完全相同的
引用变量声明的时候,必须初始化,否则报错;
所有的别名,对他们取地址,他们的地址都相同。
其他类型的引用:
-常量类型的引用
const int& a =12;
-数组的引用
int arr[12];
int (&p)[12] = arr;
//此时 p 和arr 代表的意思完全相同了
arr[0] = 687;
cout << p[0] << endl;
-指针的引用
int b = 1;
int *point = &b;
int* (&p) = point;
总结:引用变量的声明语句规则:首先这是一个引用变量(&p),其次这个是这个变量存储的数据类型type
引用和函数的关系:
引用做函数的参数
#include <iostream>
using namespace std;
void fun(int& a)
{
cout << a << endl;
}
void fun1(int a)
{
a=13;
cout << a << endl;
}
int main()
{
int b = 6;
fun(b);
fun1(b);
//两个函数运行的 本质区别是什么?
return 0;
}
区别:
fun的形参 int& a 当 调用 fun(b)的时候,是进行了一步 : int& a = b ,此时a是b的一个别名,他们的地址相同,他们均代表同一块内存空间;也就是说,我们可以在fun中,通过对a进行操作,然后更改b的值。
fun(b);
cout << b << endl;
(对比在C中是通过 地址的传递来修改值):
void fun_C(int *a)
{
*a = 13;
}
int main()
{
int b =0;
fun_C(&b);
printf("%d",b);
return 0;
}
fun1的形参是 int a 当fun(b) 的时候,相当于,把b的值赋值给a;也就是说,a和b各自代表一块空间,只不过空间里面存储的数据将相同。不能通过a的操作改变b的值。
5.1交换两个数的值
void exchange(int& a, int& b)
{
int temp =a;
a = b;
b=temp;
}
//如果不使用 引用类型 作为形参 则无法通过函数修改函数外部的变量的值。
int main()
{
int a;
int b;
cin >> a >>' ' >>b;
exchange(a,b);
cout << a\n <<b<<endl;
return 0;
}
5.2 引用 作为 返回值
int& fun()
{
int a = 687;
//注意 这里的a 是该函数内的 局部变量
return a;
}
int main()
{
int& b = fun();
//这里有警告,因为引用了 局部变量
cout << b <<endl;
//操作了 非法空间
return 0;
}
所以尽量不要引用局部变量
引用与指针的区别:
1.声明引用变量必须要初始化,指针不是必须初始化,当然我们一般int* p = NULL
2.引用初始化之后,就不能再引用其他空间了,就定死是这个变量的别名了。而指针的指向可以后期改变
3.引用不占用存储空间,指针占空间
4.引用效率更高,指针是间接操作
5.引用更安全,指针可以偏移
6.指针更灵活,直接操作地址,指针更通用:C语言和C++都行
总结 & 的三种作用:
1⃣️声明引用变量
2⃣️取地址
3⃣️数&数 ,表示位“与”运算
运算符一览
参考网址:https://www.runoob.com/cprogramming/c-operators.html
5.3增强的for循环
C++增强的点:
变量定义的位置可以在for(……)
📒VC和VS结构内的定义循环控制变量的作用域有区别:
VS(Visual Studio)中是仅针对该循环有意义
VC(Visual C++)是对下面的全部代码有作用
5.4 函数参数缺省值
函数的类型:1)无参数+无返回值
2)有参数+有返回值
3)有参数+无/有返回值;
其中3)1⃣️传值、传址
2⃣️参数缺省值/默认值
参数如何指定默认值?
一般形式:直接在形参后面用等号赋值即可
void fun(int a = 687,char c = 'A')
{
//如果不想全部赋值指定,也可以部分指定
cout << a << ' ' << c << endl;
}
部分🈯️定必须注意⚠️的规则是:
必须按照顺序“从右向左”连续指定(保证后面调用时传参位置不会错误),
不能间隔地一会指定一会不指定!
有默认值的函数调用时,可以不用传递实参
没有指定默认值的,一定要指定实参
(如果有默认值,还强制传参,会将原来的默认值覆盖)
6.1函数重载
(Cpp新增功能)
什么叫 函数重载?
同一作用域内的,函数名字相同,但是参数列表不同的 函数,互为重载函数
(特别注意:返回值,不作为函数重载的条件!)
参数列表不同又分为 :
参数类型不同
参数个数不同
什么叫同一作用域?
#include <iostream>
using namespace std;
void fun(int a)
{
cout << '1:' << a <<endl;
}
void fun(int a , int b)
{
cout << '2:' << a <<' '<<b<<endl;
}
void fun(char c)
{
cout << '3:' <<c <<endl;
}
上面三个函数的名字都一样,这在C中是绝对不允许的,但是Cpp中引入了 函数重载的概念,就允许存在了。
重载函数的 调用:
sys会根据传入的实参 ,来进行自动匹配调用哪一个函数。比如:
int main()
{
fun(2);
fun('a');
fun(5,6);
return 0;
}
📒!函数重载Tips:
float 类型的参数,传参的时候,小数后面必须加一个小f,不然默认是double类型的;
什么叫同一个作用域?
就是有共同的作用域的意思;
函数作用域:指该函数往下的所有代码;
返回值不作为函数重载的条件的意思是:
如果两个函数的返回值不同,但是名字和参数列表都相同的话,是不能构成 函数重载的,也就是说,会报错(重定义了)
默认参数和重载结合使用,可能会让调用不明确
6.3防止头文件重复包含
预处理:
宏
包含头文件
防止头文件重复包含
防止头文件重复包含:
在C语言中为了防止头文件重复包含,我们用了这个结构:
#ifndef AAA
#define AAA
//在这里写头文件的代码
void fun()
#endif
Cpp里面,我们这样:
#pragma once
//once的意思是 以下所有的代码仅编译一次
void fun()
区别?
:
前者都通用,后者取决于编译器,有的编译器不支持这个预处理
6.4类和对象的声明
什么叫 类 ?
类是一种语法。
定义:具有相同属性和行为的对象的集合
t.e.相同属性=数据成员、行为=函数
(例如:人类就是人这个对象的集合,小明是个人,也就是一个具体的个体,叫做“对象”; 物以类聚)
类和 面向对象 的关系:
面向对象是一种编程思想
类是一个语法
也就是,语法是实现思想的基础
class 和 struct 的区别?
在Cpp中,结构体struct 是一种特殊的类
#include <iostream>
using namespace std;
class CPeople
{
//类中可以包含两种类型的成员:数据成员和函数成员
int a;
void fun()
{
cout << "hello TZC"<<endl;
}
};
//声明一个类,最后面有分号
int main()
{
//声明一个自定义类的对象:类名 + 对象名
CPeople xiaoming;
//调用类中的成员,比如我们调用一下上面这个类中的hello函数:
xiaoming.fun();
//或者我们调用里面的数据变量:
xiaoming.a = 687;
cout << xiaoming.a<<endl;
return 0;
}
上面这个程序会报错,因为类中没有“访问修饰符”
编译器可能会说:无法访问private成员在‘CPeople’类中的声明
创建类之后,
⚠️!声明对象有两种:
1⃣️栈区普通对象:CPeople op;
调用成员的时候 用 对象.成员 就行了。
2⃣️堆区指针对象:
CPeople* op1 = new CPeople;
(指针对象后面必须 new一块空间给它指向,new和delete是一套,不要忘记后面还要用delete释放)
调用的成员的时候,需要用 对象->成员
类中的所有成员(个别特殊static 静态成员之外),必须通过对象访问;
意思就是说,比方说,我们不能直接把类中的函数的函数名拿出来,直接调用,这是无效的
什么叫 静态成员static?
7.1访问修饰符
关键字:
private
protected
public
友元
- public : 使类里面的成员对外可见(取消私有)
(⚠️cpp中的结构体默认是 public)
#include <iostream>
using namespace std;
class CPeople
{
public: // 访问修饰符
int a;
void fun()
{
cout << "fun=" << a <<endl;
}
};
int main()
{
CPeople op;
op.fun;
return 0;
}
private :在类中,访问修饰符默认为 private
,也就是,如果你什么关键字都不加,类里面的东西,默认私有。
作用范围:从书写关键字后面的冒号开始,一直到下一个修饰符,如果没有下一个修饰符,就到类结尾的花括号为止。
拓展功能😁:
可以作为类内成员分类的tool;
安全性
protected :受保护的;对类外不可见(主函数不可见),对子类可见;
什么叫“子类”? 有关cpp的类的继承,后面会讲。
7.3友元:
关键字:friend
针对类中的私有成员(private或者protected),如果类外函数想要使用的话,需要把想要使用的类外函数设置成 该类 的 友元函数。
方式:
友元函数:
#include <iostream>
using namespace std;
class Cstu
{
private:
int age;
void fun()
{
age = 12;
cout << age << endl;
}//给类中的变量初始化,尽量不要直接在变量后面用赋值符号=,而是在后面用一个赋值函数进行初始化值。
friend void fun1();
//设置fun1 为该类的 友元函数;此时,该类中的private私有成员对fun1可见。
};
void fun1()
{
Ctu stu;
stu.fun();//Cstu类外使用类内的私有函数,需要将fun1设置为Cstu的友元函数。
}
int main()
{
fun1();
return 0;
}
友元类:
class Cstu
{
private:
int age;
void fun()
{
age = 12;
cout << age << endl;
}
friend class CTeach;//生成友元类
};
class CTeach
{
public:
Cstu stu2;
void fun2()
{
stu2.fun();//使用Cstu中的私有fun需要 将Cteach类生成为友元类
}
};
特点:
不受访问修饰符的限制;
破坏了类的封装性,不是迫不得已不要去使用;
不使用友元,怎么访问私有成员呢?
类中的接口函数:
class Cstu
{
private:
int age;
public:
int Get()
{
return age;//实现间接调用访问
}
int Set()
{
age = 687;
}
};
利用调用public中的 Get和Set函数,来访问private中的成员
8.1构造函数
构造函数: 方便对类内的数据成员进行初始化,在声明类后,会自动调用。
#include <iostream>
using namespace std;
class CStu
{
int age;
float f;
CStu()//构造函数,函数名直接用类名,且没有返回值
{
age = 12;
f = 12.12f;
}
};
int main()
{
CStud Lihua;//创建一个类的(栈区)对象
//其中的 构造函数会在 对象创建的时候自动调用
cout << Lihua.age <<endl;
CStud *p = new CStud;// 创建一个(堆区)的对象 ;一个指针对象
//指针变量后面一定要new一块空间;
return 0;
}
Conlusion :
1.构造函数的作用
2.构造函数执行的时间
8.2构造函数的类型
(带参数列表的构造函数)
class CStu
{
int age;
float f;
CStu(int a, float b) // 带参数的构造函数,一定要记得后面创建对象的时候,要传入实际参数;当然也可以直接指定参数的默认值
{
age = a;
f =b;
}
};
int main()
{
CStu Lihua(12,6.78f);
cout << Lihua.age << endl;
//普通栈区变量的声明和输出
CStu *Xiaohua = new CStu(18,13.4f);
cout << Xiaohua->age <<endl;
//指针变量的参数传递和输出
return 0;
}
一个类里面的 构造函数 可以是多个
多个构造函数之间 是 函数重载关系
成员函数:的类外定义;
也可以在类外定义,在类内声明即可:
class CStu
{
int age;
CStu(int a );
void fun();
}
CStu::CStu(int a) //注意在前面加上类名作用域
{
age = a;
}
void CStu::fun() // 普通的成员函数的类外声明,一样的
{
cout << age << endl;
}
类外定义,主要是方便 多个头文件的时候
8.3初始化和赋值的区别
对于基本数据类型,初始化一个值和声明之后再赋值,作用效果完全一样:
int main()
{
int a = 12;//初始化
int b;
b=14;//赋值
return 0;
}
对于数组 初始化:
int a[12] = {1,2,3};//初始化可以批量赋值
对于 结构体 :
struct stu
{
int a;
int b;
};
stu c = {12,3};//初始化
c.a =12;
c.b = 3;//赋值
以上 数组和 结构体 的初始化和赋值 仅仅是形式上的区别,初始化可以减少代码量而已,作用完全一样。
对于 引用 和 const:(⚠️!!只能初始化,不能赋值):
int a = 12;
int &b = a;
const int c = 13;
9.1初始化列表
作用:给数据成员进行初始化(构造函数是“赋值”)
注意!成员初始化的顺序只和 声明的顺序有关!
形式:构造函数 名 后面直接 :变量1(初始化值),变量2……
class CStu
{
int a;
float f;
CStu () : a(18),f(60.5) //对成员a和f进行初始化
{
cout << a << f << endl;//由于我们写在了构造函数里面,所以下面我们在主函数中声明对象Lihua的时候,会自动调用
}
};
int main()
{
CStu Lihua;
return 0;
}
9.2引用和const的初始化
9.3数组和结构体使用初始化列表
class CStu
{
int a[4];
CStu(): a()
{
}
void Show()
{
for(int i =0,i<3 ,i++){
cout << a[i] << endl;
}
};