编写一个C++程序总共分为4个步骤:创建项目->创建文件->编写代码->运行程序
#include
using namespace std;
int main()
{
cout<<"hello world"<
1)单行注释:通常放在一行代码的上方,或一条语句的末尾,对该行代码说明。
//描述信息
2)多行注释:通常放在一段代码的上方,对该段代码做整体说明。
/*描述信息*/
作用:给一段指定的内存空间起名,方便操作这段内存。
语法:数据类型 变量名=初始值;
#include
using namespace std;
int main()
{
int a=10;
cout<<"a= "<
作用:用于记录程序中不可更改的数据。
1)#define 宏常量:通常在文件上方定义,表示一个常量。
#define 常量名 常量值
2)const修饰的变量:通常在变量定义前加关键字const,修饰该变量为常量,不可修改。
const 数据类型 常量名 = 常量值
关键字是C++中预先保留的单词(标识符)在定义变量或常量时,不要使用关键字。
作用:利用sizeof关键字可以统计数据类型所占内存大小
语法:sizeof(数据类型/变量)
#include
using namespace std;
int main() {
cout << "short类型所占内存空间为: " << sizeof(short) << endl;
cout << "int类型所占内存空间为: " << sizeof(int) << endl;
cout << "long类型所占内存空间为: " << sizeof(long) << endl;
cout << "long long类型所占内存空间为: " << sizeof(long long) << endl;
return 0;
}
1)标识符不能是关键字;
2)标识符只能由字母,数字,下划线组成;
3)第一个字符必须为字母或下划线;
4)标识符中字母区分大小写。
C++规定在创建一个变量或常量时,必须要指出对应的数据类型,否则无法给变量分配内存。
作用:用于表示一些不显示出来的ASCII字符
常用转义字符: \n(换行) \\(反斜线) \t(水平制表)
作用:用于从键盘获取数据
关键字:cin
语法:cin >> 变量
作用:整型变量表示的是整数类型的数据。
数据类型 占用空间
short(短整型) 2字节
int(整型) 4字节
long(长整型) Windows:4字节,Linux:4字节(32位)8字节(64位)
long long(长长整型) 8字节
整型大小比较:short < int <= long <= long long
作用:用于表示小数。
浮点类型数据变量分为两种:1)单精度 float 2)双精度 double
float f1 = 3.14f;
double d1 = 3.14;
两者的区别在于表示的有效数字范围不同:
数据类型 占用字节 有效数字范围
float 4字节 7位有效数字
double 8字节 15-16位有效数字
默认情况下,输出一个小数,会显示出6位有效数字。
作用:字符型变量用于显示单个字符。
语法:char ch = ‘a';
注意: 1)在显示字符串变量时,用单引号将字符括起来,不要用双引号;
2)单引号内只能有一个字符,不可以是字符串。
C和C++中字符型变量只占用一个字节。
字符变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放到存储单元。
作用:用于表示一串字符。
C风格字符串:char 变量名[ ] = “字符串”;
C++风格字符串:string 变量名 = “字符串值”;(使用C++风格字符串时候,要包含#include
作用:布尔数据类型代表真或假的值。
bool类型只有两个值:true—真(本质是1) flase—假(本质是0)
bool类型占1个字节大小。
作用:用于执行代码的运算。
运算符类型 作用 运算符
算术运算符 用于处理四则运算 + - * / % ++ --
赋值运算符 用于将表达式的值赋给变量 = += -= *= /= %=
比较运算符 用于表达式的比较,并返回一个真值或假值 ==
逻辑运算符 用于根据表达式的值返回真值或假值 !(与) &&(与) ||(或)
C/C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构。
顺序结构:程序按顺序执行,不发生跳转;
选择结构:依据条件是否满足,有选择的执行相应功能;
循环结构:依据条件是否满足,循环多久执行某段代码。
1)if语句
作用:执行满足条件的语句
单行格式if语句:
if(条件){
条件满足执行的语句
};
多行格式if语句:
if(条件){
条件满足执行的语句
}
else{
条件不满足执行语句
};
多条件的if语句:
if(条件1){
条件1满足执行的语句
}
else if(条件2){
条件2满足执行的语句
}
else{
都不满足执行的语句
}
2)嵌套if语句
在if语句中,可以嵌套使用if语句,达到更精准的条件判断。
3)三目运算符
作用:通过三目运算符实现简单的判断
语法:表达式1 ?表达式2:表达式3
如果表达式1的值为真,执行表达式2,并返回表达式2的结果;
如果表达式1的值为假,执行表达式3,并返回表达式3的结果。
4)switch语句
作用:执行多条件分支语句。
switch(表达式)
{
case结果1:
执行语句;
break;
case结果2:
执行语句;
break;
...
default:
执行语句;
break;
}
作用:满足循环条件,执行循环语句。
while(循环条件){
循环语句
};
作用:满足循环条件,执行循环语句。
do{
循环语句
}
while(循环条件);
注意:与while的区别在于do-while会先执行一次循环语句,再判断循环条件。
//在屏幕输出0-9
#include
using namespace std;
int main() {
int num = 0;
do
{
cout << num << endl;
num++;
} while (num<10);
return 0;
}
//水仙花数,每个位数上的数字的3次幂之和等于它本身
#include
using namespace std;
int main() {
int num = 100;//要判断的三位数
int a = 0; //个位
int b = 0; //十位
int c = 0; //百位
do {
a = num % 10; //获取该三位数的个位
b = num / 10 % 10; //获取该三位数的十位
c = num / 100; //获取该三位数的百位
if (a * a * a + b * b * b + c * c * c == num) {
cout << num << "是水仙花数" << endl;
}
num++;
} while (num < 1000);
system("pause");
return 0;
}
作用:满足循环条件,执行循环语句。
for(起始表达式;条件表达式;末尾循环体){
循环语句;
}
注意:for循环体中的表达式,需要用分号进行分隔。
作用:在循环中再嵌套一层循环,解决一些实际问题。
//乘法口诀表
#include
using namespace std;
int main() {
for (int i = 1; i < 9; i++)
{
for (int j = 0; j <= i; j++)
{
cout << j << "*" << i << "=" << j * i << " ";
}
cout << endl;
};
return 0;
}
作用:用于跳出选择结构或循环结构
break使用时机:
出现在switch语句中,用于终止case并跳出switch;
循环语句中,跳出当前的循环语句;
嵌套语句中,跳出最近的内层循环。
作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环。
注意:continue并没有使整个循环终止,而break会跳出循环。
所谓数组,就是一个集合,里面存放了相同类型的数据元素。
特点:1)数组中每个数据元素都是相同的数据类型;
2)数组是由连续的内存位置组成的。
一维数组定义方式:
数据类型 数组名[数组长度];
数据类型 数组名[数组长度] = {值1,值2,...}
数据类型 数组名[] = {值1,值2,...}
#include
using namespace std;
int main() {
int arr[5];
int arr2[5] = { 10,20,30,40,50 };
//利用循环输出数组中的元素
for (int i = 0; i < 5; i++)
{
cout << arr2[i] << endl;
}
return 0;
}
数组名的命名规范与变量名命名规范一致,不要和变量名重名。
数组中下标是从0开始索引。
一维数组数组名用途:
可以统计整个数组在内存中的长度 => sizeof(arr)
可以获取数组在内存中的首地址
cout<<"数组首地址为:"<< (int)arr << endl;
cout<<"数组中第一个元素地址为:"<< (int)&arr[0] << endl;
二维数组就是在一维数组上,多加一个维度。
4种定义方式:
数据类型 数组名[行数][列数];
数据类型 数组名[行数][列数] = {{数据1,数据2},{数据3,数据4}};
数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4};
数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4};
二维数组名组名用途:1)查看二维数组所占内存空间;2)获取二维数组首地址
作用:将一段经常使用的代码封装起来,减少重复代码。
函数定义的步骤:
返回值类型 -> 函数名 -> 参数列表 -> 函数体语句 -> return表达式
语法:
返回值类型 函数名(参数列表)
{
函数体语句;
return表达式;
}
int add(int num1,int num2){
int sum = num1 + num2;
return sum;
}
返回值类型:一个函数可以返回一个值,在函数定义中。
函数名:给函数起的名称。
参数列表:使用该函数时,传入的数据。
函数体语句:花括号内的代码,函数内需要执行的语句。
return表达式:和返回值类型挂钩,函数执行完后,返回相应的数据。
功能:使用定义好的函数。
语法:函数名(参数)
所谓值传递,就是在函数调用时实参将数值传入给形参。
值传递时,如果形参发生改变,并不会影响实参。
void swap(int num1,int num2){
int temp = num1;
num1 = num2;
num2 = temp;
cout <<"交换后"<
无参无返 有参无返 无参无返 有参有返
作用:告诉编译器函数名称及如何调用函数,函数主体可以单独定义。
函数的声明可以多次,但函数的定义只能有一次。
作用:让代码结构更加清晰。
函数分文件编写一般步骤:
1)创建后缀名为.h的头文件;
2)创建后缀名为.cpp的源文件;
3)在头文件中写函数的声明;
4)在源文件中写函数的定义。
作用:可以通过指针间接访问内存
内存编号是从0开始记录的,一般用十六进制数字表示。
可以利用指针变量保存地址。
语法:数据类型 * 指针变量名
//定义指针
int a = 10;
int *p;
//让指针记录变量a的地址
p = &a;
使用指针:可以通过引用的方式来找到指针指向的内存。
*p; //指针前加*代表解引用,找到指针指向的内存中的数据
指针所占内存的空间:32位占用4个字节空间; 64位占用8个字节空间
空指针:指针变量指向内存中编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的。
//指针变量p指向内存地址编号为0的空间
int *p = NULL;
//访问空指针报错,内存编号0-255为系统占用内存,不允许用户访问。
cout << *p << endl;
const修饰指针的三种情况:
const修饰指针 ——常量指针
const修饰常量 ——指针常量
const即修饰指针,又修饰常量
常量指针特点:指针的指向可以修改,但指针指向的值不可以改。
int a = 10;
int b = 10;
const int *p = &a;
*p = 20;//错
p = &b;//对
指针常量特点:指针的指向不可以改,指针指向的值可以改。
int a = 10;
int b = 10;
int *const p = &a;
*p = 20;//对
p = &b;//错
const即修饰指针,又修饰常量:指针的指向和指针指向的值都不可以改。
技巧:看const右侧紧跟的是指针还是常量,是指针就是常量指针,是常量就是指针常量。
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
语法:
struct 结构体名{
结构体成员列表
};
通过结构体创建变量的方式有3种:
struct 结构体名 变量名
struct 结构体名 变量名 = {成员1值,成员2值,...}
定义结构体时顺便创建变量
struct Student{
string name;
int age;
int score;
}
struct Student s1;
//给s1赋值,通过.访问结构体变量中的属性
s1.name = "张三";
s1.age = 18;
s1.score = 100;
struct Student s2 = {"李四",19,80};
struct Student{
string name;
int age;
int score;
}s3;
s3.name = "王五";
s3.age = 20;
s3.score = 60;
总结: 定义结构体时的关键字是struct,不可省略;
结构体变量利用操作符“.”访问成员。
struct和class的区别:在C++中struct和class唯一的区别在于默认的访问权限不同。struct默认权限为公有,class默认私有。
作用:将自定义的结构体放到数组中方便维护。
语法:struct 结构体名 数组名[元素个数] = {{},{},{},...}
struct Student{
string name;
int age;
int score;
}
//创建结构体数组
struct Student stuArr[3] = {{"刘",22,100},{"陈",22,100},{"池",22,100}};
作用:通过指针访问结构体中的成员。
利用操作符“ -> ”可以通过结构体指针访问结构体属性。
//1、创建学生结构体
struct Student s = {"liu",22,100};
//2、通过指针指向结构体
struct Student *p = &s;
//3、通过指针访问结构体变量中的数据
cout <<"姓名是:"<< p->name <
作用:结构体中的成员可以是另一个结构体。
//每个老师辅导一个成员,一个老师的结构体中,记录一个学生的结构体
struct Student{
string name;
int age;
int score;
};
struct Teacher{
int id;
string name;
int age;
struct Student stu;//子结构学生
};
//创建老师
int main(){
Teacher t;
t.id = 1000;
t.name = "wu";
t.age = 40;
t.stu.name = "liu";
t.stu.age = 22;
t.stu.score = 80;
}
作用:将结构体作为参数向函数中传递
传递方式: 值传递 地址传递
struct Student{
string name;
int age;
int score;
};
//值传递
void printStudent(student stu){
stu.age = 22;
cout<<"子函数中 年龄:"<< stu.age << endl;
}
//地址传递
void printStudent2(struct Student *p){
cout<<"年龄:"<< p->age <
如果不想修改主函数中的数据,用值传递,反之用地址传递。
C++程序在执行时,将内存大方向划分为4个区域。
代码区:存放函数体的二进制代码,由操作系统进行管理。
全局区:存放全局变量和静态变量以及常量。
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域:
1)代码区:存放CPU执行的机器指令。代码区是共享的,只读的。
2)全局区:存放全局变量、静态变量、常量。该区域的数据在程序结束后由操作系统释放。
1)栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。
2)堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
作用:给变量起别名。
语法:数据类型 &别名 = 原名
int a = 10;
int &b = a;
b = 20;
cout << a << endl; //输出也为20
注意:1)引用必须初始化;2)引用在初始化后,不可以改变。
int a = 10;
int b = 20;
//int &c; //错误,必须初始化
int &c = a;//一旦初始化,就不可以更改
c = b;//这是赋值操作,不是更改引用
引用的本质:引用的本质在C++内部实现是一个指针常量。
作用:函数传参时,可以利用引用让形参修饰实参。
//1、值传递,形参不会修饰实参
void mySwap1(int a,int b){
int temp = a;
a = b;
b = temp;
}
//2、地址传递,形参修饰实参
void mySwap2(int *a,int *b){
int temp = *a;
*a = *b;
*b = temp;
}
//3、引用传递,形参会修饰实参
void mySwap3(int &a,int &b){
int temp = a;
a = b;
b = temp;
}
//总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更加清晰。
语法: 返回值类型 函数名(参数 = 默认值){}
int func(int a, int b = 10, int c =10){
return a + b + c;
}
注意: 1)如果某个函数参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值;
2)如果函数声明有默认值,函数实现的时候就不能有默认参数。
作用:函数名可以相同,提高复用性。
函数重载满足条件:1)同一个作用域下;2)函数名相同;3)函数参数类型不同或个数不同或顺序不同
C++面向对象三大特性:封装、继承、多态。C++认为万事万物都皆为对象,对象上有其属性和行为。
封装的意义:1)将属性和行为作为一个整体,表现生活中的事物
2)将属性和行为加以权限控制
封装意义一:在设计类的时候,属性和行为写在一起,表现事物。
语法:class 类名{访问权限:属性/行为};
//设计一个圆类,求圆的周长
const double PI = 3.14;
class Circle{
public: //公共权限
int m_r; //属性-半径
deouble calculatez(){
return 2 * PI * m_r; //行为-获取圆的周长
}
};
int main(){
Circle c1; //通过圆类,创建具体的圆(对象)
c1.m_r = 10; //给圆对象的属性进行赋值
cout <<"圆的周长:"<< c1.calculatez() << endl;
}
类中的属性和行为,统一称为成员。
属性:成员属性,成员变量。 行为:成员函数,成员方法。
封装意义二:类在设计时,可以把属性和行为放在不同的权限下,加以控制。
三种权限:
公共权限 public 类内可以访问 类外可以访问
保护权限 protected 类内可以访问 类外不可以访问
私有权限 private 类内可以访问 类外不可以访问
成员属性设置为私有优点:
将所有成员属性设置为私有,可以自己控制读写权限;
对于写权限,我们可以检测数据的有效性
主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
语法:类名(){}
构造函数没有返回值也不写void;
函数名称与类名相同;
构造函数可以有参数,因此可以发生重载;
程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次。
主要作用于对象销毁前系统自动调用,执行一些清理工作。
语法: ~类名(){}
析构函数没有返回值也不写void;
函数名称与类名相同,在名称前面加上符号~;
析构函数不可以有参数,因此不能发生重载;
程序在对象销毁前会自动调用析构函数,无须手动调用,而且只会调用一次。
class Person{
public:
Person(); //构造函数
{
cout <<"Person构造函数调用"<< endl;
}
~Person(); //析构函数
{
cout <<"Person析构函数调用" << endl;
}
}
两种分类方式:
1)按参数分为:有参构造和无参构造
2)按类型分为:普通构造和拷贝构造
三种调用方式:1)括号法; 2)显示法; 3)隐式转换法
//拷贝构造函数
Person(const Person &p){
age = p.age; //将传入的人身上的所有属性拷贝到我身上
}
//调用
Person p1; //默认构造函数调用 注意:调用默认参数时,不要加()
Person p2; //有参构造函数
Person p3; //拷贝构造函数
//显示法
Person p2 = Person(10); //有参构造
Person p3 = Person(p2); //拷贝构造 注意:不要利用拷贝构造函数,初始化匿名对象
//隐式转换法
Person p4 = 10; //相当于Person p4 = Person(10);
C++中拷贝构造函数调用时机通常有三种情况:
使用一个已经创建完毕的对象来初始化一个新对象;
值传递的方式给函数参数传值;
以值方式返回局部对象。
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作。
浅拷贝带来的问题:堆区的内存重复释放。
浅拷贝的问题要利用深拷贝进行解决:
m_Height = new int(*p.m_Height);
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
作用:C++提供了初始化列表语法,用来初始化属性。
语法:构造函数():属性1(值1),属性2(值2)...{}
class Person{
public:
Person(int a,int b,int c){
m_a = a;
m_b = b;
m_c = c;
}
}
//初始化列表赋初值
Person():m_a(10), m_b(20), m_c(30){}
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。
静态成员分为:
1)静态成员变量:所有对象共享同一份数据;在编译阶段分配内存;类内声明,类外初始化。
2)静态成员函数:所有对象共享同一个函数;静态成员函数只能访问静态成员变量。
1)常函数:成员函数后加const后我们称这个函数为常函数
常函数内一般不可修改成员属性,成员函数声明时加关键字mutable后,在常函数中依然可以修改。
2)常对象:声明对象前加const称该对象为常对象。
常对象只能调用常函数。
友元的目的是让一个函数或类访问另一个类中的私有成员。
友元的关键字:friend
友元的三种实现:1)全局函数做友元;2)类做友元;3)成员函数做友元
//1、全局函数做友元
class Building{
friend void goodGay(Buliding *buliding);
}
//2、类做友元
class Building{
friend class GoodGay; //GoodGay类是本类的好朋友,可以访问本类中私有成员
}
//3、成员函数做友元
class Building{
friend void GoodGay::visit();
...
}
继承的好处:减少重复代码。
语法:class 子类:继承方式 父类
子类也称为派生类;父类也称为基类。
派生类的成员,包含两大部分:一类是从基类继承过来的;一类是自己增加的成员。
从基类继承过来的表现其共性,新增成员体现其个性。
继承方式一共有3种:公共继承;保护继承;私有继承
父类所有非静态成员属性都会被子类继承下去。
子类继承父类后,当创建子类对象,也会调用父类的构造函数。
继承中构造和析构函数顺序:先构造父类,再构造子类,析构顺序与构造顺序相反。
访问子类同名成员:直接访问即可。
s.m_a
访问父类同名成员:需要加作用域。
s.Base::m_a
小结:1)子类对象可以直接访问子类中同名成员;
2)子类对象加作用域可以访问到父类同名成员;
3)当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数。
C++允许一个类继承多个类。
语法:class 子类:继承方式 父类1,继承方式 父类2,...
多继承可能会引发父类中有同名成员出现,需要加作用域区分。实际开发不建议使用多继承。
多态定义: 同一种行为(调用)导致的不同的结果。
虚函数:虚函数是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
#include
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <area();
// 存储三角形的地址
shape = &tri;
// 调用三角形的求面积函数 area
shape->area();
return 0;
}
多态分类:1)静态多态:函数重载和运算符重载都属于静态多态,复用函数名;
2)动态多态:派生类和虚函数实现运行多态。
静态多态和动态多态的区别:
静态多态的函数地址早绑定——编译阶段确定函数地址;
动态多态的函数地址晚绑定——运行阶段确定函数地址。
动态多态满足条件:1)有继承关系;2)子类需要重写父类虚函数
动态多态的使用:父类的指针或指引指向子类对象。
Base *base = new son;
重写:函数返回值类型 函数名 参数列表完全一致称为重写。
多态优点:1)代码组织结构清晰;
2)可读性强;
3)利于前期和后期的拓展和维护。