有C语言基础,想学习c++,记了一些主要的基础语法。希望这个笔记可以帮助相同目的的你。主要参考了(大部分是复制粘贴)斧头帮-帮主(课程还是很不错的)的课程讲义,建议看完笔记,再敲下每个阶段最后的实战视频,助你快速掌握c艹
基础语法
字符串
C语言风格
char 变量名[] = "字符串值"
c++风格
string 变量名 = "字符串值"
注意:C++风格字符串,需要加入头文件==#include==
输入数据
cin
cin >> 变量;
输出数据
cout
ciout<<"字符串"<<变量<<endl;
endl有换行 以及 清空缓冲区的作用;
算数运算符
/ 除数不能为零
两个小数不能取余
const修饰指针
- const修饰指针 — 常量指针
- const修饰常量 — 指针常量
- const即修饰指针,又修饰常量
const修饰的是指针,指针指向可以改,指针指向的值不可以更改
const修饰的是常量,指针指向不可以改,指针指向的值可以更改
const既修饰指针又修饰常量
技巧:看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量
结构体
在结构体中可以定义另一个结构体作为成员,用来解决实际问题
通讯录管理系统
- 实现了基本功能,部分细节没写
/*******************************************/
/****************通讯录管理系统************/
/******************************************/
/*
1、添加联系人
2、显示联系人
3、删除联系人
3、查找联系人
4、修改联系人
5、清空联系人
6、退出通讯录
*/
#include <iostream>
#include <string>
using namespace std;
#define MAX 100
struct Person //联系人结构体
{
string name;
int sex;
int age;
string PhoNum;
string addr;
};
struct AddressBook
{
struct Person PersonArry[MAX];
int size; //表示当前通讯录人员个数
};
//菜单功能
void ShowMenu()
{
cout << "*************************"<<endl;
cout << "***** 1、添加联系人 *****" << endl;
cout << "***** 2、显示联系人 *****" << endl;
cout << "***** 3、删除联系人 *****" << endl;
cout << "***** 4、修改联系人 *****" << endl;
cout << "***** 5、清空联系人 *****" << endl;
cout << "***** 6、查找联系人 *****" << endl;
cout << "***** 0、退出通讯录 *****" << endl;
cout << "**************************"<<endl;
}
//添加联系人
void addPerson(AddressBook * abs)
{
//判断通讯录是否满了
if (abs->size==MAX)
{
cout << "通讯录已满" << endl;
return ;
}
else
{
//添加信息
cout << "名字" << endl;
cin >> abs->PersonArry[abs->size].name;
cout << "性别" << endl;
cin >> abs->PersonArry[abs->size].sex;
cout << "年龄" << endl;
cin >> abs->PersonArry[abs->size].age;
cout << "号码" << endl;
cin >> abs->PersonArry[abs->size].PhoNum;
cout << "地址" << endl;
cin >> abs->PersonArry[abs->size].addr;
//更新通讯录人数
abs->size++;
//清屏
system("cls");
}
}
//显示所有联系人
void ShowPerson(AddressBook * abs)
{
int i = 0;
for ( i = 0; i < abs->size; i++)
{
cout << "联系人" << i << endl;
cout << " 名字 " << abs->PersonArry[i].name << " 号码 " << abs->PersonArry[i].PhoNum << " 性别 " << abs->PersonArry[i].sex << \
" 年龄 " << abs->PersonArry[i].age << " 地址 " << abs->PersonArry[i].addr << endl;
}
}
//判断联系人是否存在
int isExisst(AddressBook * abs,string m_name)
{
for (int i = 0; i < abs->size; i++)
{
if (abs->PersonArry[i].name==m_name)
{
return i; //返回联系人下标
}
}
return -1; //未找到联系人
}
//删除联系人
void DelPerson(AddressBook * abs)
{
string name;
int k;
cout << "请输入您要删除的联系人" << endl;
cin >> name;
k = isExisst(abs, name);
if (k==-1)
{
cout << "查无此人" << endl;
}
else
{
for (int i = k; i < abs->size; i++)
{
abs->PersonArry[i] = abs->PersonArry[i + 1];
}
abs->size--;
cout << "删除成功" << endl;
}
//清屏
system("cls");
}
//查找联系人
void SearchPerson(AddressBook * abs)
{
string name;
int i;
cout << "请输入您要查找的联系人" << endl;
cin >> name;
i = isExisst(abs, name);
if (i == -1)
{
cout << "查无此人" << endl;
}
else
{
cout << " 名字 " << abs->PersonArry[i].name << " 号码 " << abs->PersonArry[i].PhoNum << " 性别 " << abs->PersonArry[i].sex << \
" 年龄 " << abs->PersonArry[i].age << " 地址 " << abs->PersonArry[i].addr << endl;
}
//清屏
system("cls");
}
//修改联系人
void FixPerson(AddressBook * abs)
{
string name;
int i;
cout << "请输入您要修改的联系人" << endl;
cin >> name;
i = isExisst(abs, name);
if (i == -1)
{
cout << "查无此人" << endl;
}
else
{
//修改信息
cout << "名字" << endl;
cin >> abs->PersonArry[i].name;
cout << "性别" << endl;
cin >> abs->PersonArry[i].sex;
cout << "年龄" << endl;
cin >> abs->PersonArry[i].age;
cout << "号码" << endl;
cin >> abs->PersonArry[i].PhoNum;
cout << "地址" << endl;
cin >> abs->PersonArry[i].addr;
}
}
//清空联系人
void ClearPerson(AddressBook * abs)
{
abs->size = 0;
cout << "清空成功!" << endl;
}
int main()
{
//创建通讯录
AddressBook abs;
abs.size = 0;
int select = 0;
while (1)
{
ShowMenu();
cin >> select;
switch (select)
{
case 1: //1、添加联系人
addPerson(&abs);
break;
case 2: //2、显示联系人
ShowPerson(&abs);
break;
case 3: //3、删除联系人
DelPerson(&abs);
break;
case 4: //4、修改联系人
FixPerson(&abs);
break;
case 5: //5、清空联系人
ClearPerson(&abs);
break;
case 6: //6、查找联系人
SearchPerson(&abs);
break;
case 0://退出通讯录
cout << "欢迎再次使用通讯录" << endl;
system("pause");
return 0;
break;
default:
break;
}
}
system("pause");
return 0;
}
面向对象
双冒号作用域运算符
可以访问到同名的全局变量
int k=1;
void w()
{
int k=1; //局部变量
::k=2;//全局变量
}
namespace 参考链接
-命名空间下可以储存 变量、函数、类…
-命名空间必须声明在全局作用域下
-命名空间 可以嵌套命名空间
-命名空间是开放的,可以随时向命名空间中添加新成员
-命名空间可以是匿名的,没有名字相当于全局的、可以直接访问
-命名空间可以起别名,方法:namespace 别名=已经存在命名空间的名字;
using声明
using声明和揪心原则同时出现会报错
using编译指令
编译指令和就近原则,优先使用就近原则
内存分区模型
C++程序在执行时,将内存大方向划分为4个区域:
内存四区意义:
不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程
new关键字,在堆区开辟内存,返回地址
int *p=new int (10)
参考 new用法
开辟数组 int* arr = new int[10];
new和malloc
使用delete 释放内存
释放数组 delete 后加 [] delete[] arr; 释放数组时要加一个中括号
引用
作用: 给变量起别名
语法: 数据类型 &别名 = 原名 就是起别名
~~(相当于再定义一个指针,指向变量) ~~ 这句话不对
注意事项:引用必须初始化
引用在初始化后,不可以改变
//建立对数组的引用
int arr[5]={1,2,3,4,5};
//1、先定义数组类型,再通过数组类型 定义数组引用
typedef int(ARRAY_TYPE)[5];
ARRAY_TYPE &别名 = arr;int
//2、先定义数组引用类型 再通过数组引用类型 定义数组引用
typedef int(&引用)[5];
引用 别名 =arr;
//3、直接定义数组引用
int (& pArr)[5]=arr;
引用作为函数参数,形参会修饰实参
//参数传递:引用传递
//引用传递它减少了数据的复制, 可以提高程序的执行效率
//引用传递操作的就是实参本身
引用做函数的返回值 用法:函数调用作为左值 注意:不要返回局部变量的引用 函数的调用可以作为左值
语法:类型 &函数名(形参列表){ 函数体 }
1.引用作为函数的返回值时,必须在定义函数时在函数名前将&
2.用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本
指针与引用的区别
-指针是可以独立存在的; 但是引用不行
-引用必须要进行初始化,指针没有必要
-指针可以设置为NULL, 但是引用不行
-引用一旦进行初始化之后,不会再改变其指向;但指针可以
内联函数
关键字 inline
解决了宏的缺陷,因为是普通函数,又带来了宏的有点
函数的声明和函数的实现要同时拥有inline才算内联函数
以空间换时间
类内部的成员函数
内联函数详解
函数默认参数
在C++中,函数的形参列表中的形参是可以有默认值的。
语法: 返回值类型 函数名 (参数= 默认值){}
注意事项:
- 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
- 如果函数声明有默认值,函数实现的时候就不能有默认参数
函数占位参数
C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
语法: 返回值类型 函数名 (数据类型){}
函数重载
(通俗点说,就是孙悟空和六耳猕猴,长得一摸一样,但功能用处不一样,细节不一样)
作用:函数名可以相同,提高复用性
函数重载满足条件:
同一个作用域下
函数名称相同
函数参数类型不同 或者 个数不同 或者 顺序不同
注意: 函数的返回值不可以作为函数重载的条件
###类和对象
C++面向对象的三大特性为: 封装、继承、多态
C++认为 万事万物都皆为对象 ,对象上有其属性和行为
‘4.1.1 封装的意义
封装是C++面向对象三大特性之一
封装的意义:
将属性和行为作为一个整体,表现生活中的事物
}void func2(int a) { cout << “func2(int a) 调用” << endl; }int main() { int a = 10; func(a); //调用无const func(10);//调用有const //func2(10); //碰到默认参数产生歧义,需要避免 system(“pause”); return 0; }
将属性和行为加以权限控制
封装意义一:
在设计类的时候,属性和行为写在一起,表现事物
语法: class 类名{ 访问权限: 属性 / 行为 };
封装意义二:
类在设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:
- public 公共权限
- protected 保护权限
- private 私有权限
公共权限 public 类内可以访问 类外可以访问
保护权限 protected 类内可以访问 类外不可以访问
私有权限 private 类内可以访问 类外不可以访问
struct和class区别
在C++中 struct和class唯一的区别就在于 默认的访问权限不同
区别:
struct 默认权限为公共
class 默认权限为私有
构造函数和析构函数
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法: 类名(){} 1. 构造函数,没有返回值也不写void
2. 函数名称与类名相同
3. 构造函数可以有参数,因此可以发生重载
4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数语法: ~类名(){} 1. 析构函数,没有返回值也不写void
5. 函数名称与类名相同,在名称前加上符号 ~
6. 析构函数不可以有参数,因此不可以发生重载
7. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
构造函数调用规则
默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空) 2.默认析构函数(无参,函数体为空) 3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数,c++不会再提供其他构造函数
深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
初始化列表
作用:
C++提供了初始化列表语法,用来初始化属性
语法:
构造函数():属性1(值1),属性2(值2)… {}
类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为 对象成
class A {}
class B {
A a;
}
B类中有对象A作为成员,A为对象成员
//当类中成员是其他类对象时,我们称该成员为 对象成员
//构造的顺序是 :先调用对象成员的构造,再调用本类构造
//析构顺序与构造相反
(简单来讲,就是 我 调用 他
先执行 他 的构造,再执行我的
结束时,先执行 我的析构,后 他的)
explicit 关键字
不让用户隐式调用explicit 关键字
静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分为:
静态成员变量
所有对象共享同一份数据
在编译阶段分配内存
类内声明,类外初始化
静态成员函数
所有对象共享同一个函数
静态成员函数只能访问静态成员变量
C++对象模型和this指针
成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
1、非静态成员变量占对象空间
2、静态成员变量不占对象空间
3、函数也不占对象空间,所有函数共享一个函数实例
4、静态成员函数也不占对象空间
** this指针概念**
c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途:
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用return *this
空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
const修饰成员函数
常函数:
成员函数后加const后我们称为这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
声明对象前加const称该对象为常对象
常对象只能调用常函数
友元
友元的关键字为 friend
友元的三种实现:
1.全局函数做友元
2.类做友元
3.成员函数做友元
主要用于其他函数、成员等访问 类里面的私有方法
运算符重载
operator+
继承
class A : public B;
A 类称为子类 或 派生类
B 类称为父类 或 基类
**派生类中的成员,包含两大部分**:
一类是从基类继承过来的,一类是自己增加的成员。
从基类继承过过来的表现其共性,而新增的成员体现了其个性。
继承的语法: class 子类 : 继承方式 父类
继承方式一共有三种:
公共继承
保护继承
私有继承
多继承语法
C++允许一个类继承多个类
语法: class 子类 :继承方式 父类1 , 继承方式 父类2…
多继承可能会引发父类中有同名成员出现,需要加作用域区分
模板
C++另一种编程思想称为 泛型编程 ,主要利用的技术就是模板
C++提供两种模板机制:函数模板和类模板
函数模板语法
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
template 函数声明或定义
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
函数模板利用关键字 template
使用函数模板有两种方式:自动类型推导、显示指定类型
模板的目的是为了提高复用性,将类型参数化
自动类型推导,必须推导出一致的数据类型T,才可以使用
模板必须要确定出T的数据类型,才可以使用
typename可以替换成class
普通函数与函数模板区别:
普通函数调用时可以发生自动类型转换(隐式类型转换)
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
如果利用显示指定类型的方式,可以发生隐式类型转换
总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T
普通函数与函数模板的调用规则
调用规则如下:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
总结:
利用具体化的模板,可以解决自定义类型的通用化
学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
类模板
类模板语法
类模板作用:
建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表。
语法:
template
类
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
类模板与函数模板区别主要有两点:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
类模板中成员函数和普通类中成员函数创建时机是有区别的:
普通类中的成员函数一开始就可以创建
类模板中的成员函数在调用时才创建
当类模板碰到继承时,需要注意一下几点:
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
如果不指定,编译器无法给子类分配内存
如果想灵活指定出父类中T的类型,子类也需变为类模板
总结:如果父类是类模板,子类需要指定出父类中T的数据类型
类模板分文件编写
解决方式1:直接包含.cpp源文件
解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制