变量存在的意义:方便我们管理内存空间
变量创建的语法:数据类型 变量名称 = 变量初始值;
int main()
{
char c;
unsigned char uc;
short s;
int i;
long l;
long long ll;
float f;
double d;
}
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short | 2字节 | (-2^15-2^15-1) 即(-32768,32767) |
int | 4字节 | (-2^31-2^31-1) |
long | windows 为4字节,linux为4字节(32位)、8字节(64位)节 | (-2^31-2^31-1) |
long long | 2字节 | (-2^63-2^63-1) |
float | 4字节 | 7位有效数字 |
double | 8字节 | 15-16位有效数字 |
用于记录程序中不可更改的数据
C++定义常量的两种方式:
①:#define 宏常量:#define 常量名 常量值,通常定义在文件上方
#include
#define PI 3.14
int main()
{
std::cout << PI << std::endl;
}
②:const修饰的常量:const 数据类型 常量名 = 常量值,通常在变量定义前加关键字const,修饰该变量为常量,不可修改
#include
int main()
{
const float Pi = 3.14;
std::cout << Pi << std::endl;
}
#include
int main()
{
int a;
std::cout << "请输入一个整数值:" << std::endl;
std::cin >> a;
std::cout << "您输入的值为:" << a << std::endl;
}
将一段经常使用的代码封装起来,减少重复代码
语法:
返回值类型 函数名(参数列表)
{
函数体语句
返回值语句
}
C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
while循环
#include
int main()
{
std::cout << "模拟while循环,循环输出倒三角,请您输入要输出多少行的三角:" << std::endl;
int i_line;
std::cin >> i_line;
while (i_line > 0)
{
int count = i_line;
while (count >0)
{
std::cout << '*';
count--;
}
std::cout <<std::endl;
i_line--;
}
}
while循环模拟一个控制台菜单的选择,选择退出后菜单退出,否则仅输出选择了菜单的什么项:
#include
int main()
{
while (true)
{
std::cout << "请选择:\n a:开始游戏\n b:继续游戏\c c:退出" << std::endl;
char chose;
std::cin >> chose;
if (chose == 'a') {
std::cout << "您选择了开始游戏" << std::endl;
}
if (chose == 'b') {
std::cout << "您选择了继续游戏" << std::endl;
}
if (chose == 'c') {
std::cout << "您选择了退出" << std::endl;
break;
}
}
}
案例:水仙花数
#include
int main()
{
std::cout << "水仙花数案例实现:输出1-10000中所有数的水仙花数" << std::endl;
int max = 10000;
int number = 1;
while (number <= max)
{
int sub_number = number;
int digit_number = 0;
while (sub_number>0)
{
sub_number = sub_number / 10;
digit_number++;
}
int count = 0;
int digit = digit_number;
sub_number = number;
while (digit_number > 0)
{
int sub = sub_number - sub_number / 10 * 10;
count += pow(sub, digit);
sub_number = sub_number / 10;
digit_number--;
}
if (count == number) {
std::cout << number << std::endl;
}
number++;
}
}
for循环
#include
int main()
{
std::cout << "输出九九乘法表" << std::endl;
for (int line = 1; line <= 9; line++)
{
for (int col = 1; col <= line; col++)
{
std::cout << col << "*" << line << " = " << line * col << " ";
}
std::cout << std::endl;
}
}
案例:敲桌子,从1开始数到数字100,如果数字个位含有7,或者十位含有7,或者该数字是7的倍数,我们打印敲桌子,否则仅输出该数字
#include
int main()
{
std::cout << "从1开始数到数字100,如果数字个位含有7,或者十位含有7,或者该数字是7的倍数,我们打印敲桌子,否则仅输出该数字" << std::endl;
for (int number = 1; number <= 100; number++) {
//该数字是7的倍数,我们打印敲桌
if (number % 7 == 0)
{
std::cout << "敲桌子" << std::endl;
}
else if (number % 10 == 7)
{
std::cout << "敲桌子" << std::endl;
}
else if ((number/10) % 10 == 7)
{
std::cout << "敲桌子" << std::endl;
}
else
{
std::cout << number << std::endl;
}
}
}
案例:有三个数字,使用条件分支实现获得三个数字中的最大值
#include
int main()
{
int a, b, c;
std::cout << "请输入第一个数字的值" << std::endl;
std::cin >> a;
std::cout << "请输入第二个数字的值" << std::endl;
std::cin >> b;
std::cout << "请输入第三个数字的值" << std::endl;
std::cin >> c;
std::cout << "您输入的三个数值为" << a <<"," << b << "," << c << std::endl;
//使用条件分支获取最大的数值
if (a > b)
{
if (a > c)
{
std::cout << "您输入的最大值是:" << a << std::endl;
}
else
{
std::cout << "您输入的最大值是:" << c << std::endl;
}
}
else
{
if (b > c)
{
std::cout << "您输入的最大值是:" << b<< std::endl;
}
else
{
std::cout << "您输入的最大值是:" << c << std::endl;
}
}
}
C++中三目运算符返回的是变量,可以继续赋值
#include
int main()
{
int a, b, c;
std::cout << "请输入第一个数字的值" << std::endl;
std::cin >> a;
std::cout << "请输入第二个数字的值" << std::endl;
std::cin >> b;
c = a > b ? a : b = 100;
std::cout << "a = " << a << ",b = " << b << std::endl;
std::cout << "您输入的最大的数值为" << c << std::endl;
}
#include
int main()
{
int a, b, c;
std::cout << "请输入第一个数字的值" << std::endl;
std::cin >> a;
std::cout << "请输入第二个数字的值" << std::endl;
std::cin >> b;
c = a > b ? a : b;
std::cout << "您输入的最大的数值为" << c << std::endl;
}
#include
int main()
{
char chose;
std::cout << "请输入您的选择" << std::endl;
std::cout << "a:仅输出字符a;\nb:输出字符b与字符c;\nc:输出字符c;\n 其它输入则提示错误" << std::endl;
std::cin >> chose;
switch (chose)
{
case 'a': std::cout << "您输入的是字符a" << std::endl; break;
case 'b': std::cout << "您输入的是字符b" << std::endl;
case 'c': std::cout << "您输入的是字符c" << std::endl; break;
default:
std::cout << "您输入的字符不符合规范" << std::endl;
}
}
#include
void Print(int* array, int length);
void Bubble_Sort(int* array, int length);
void Selection_Sort(int* array, int len);
void Insertion_Sort(int* array, int length);
void Swap(int* array, int index_f, int index_e);
int main()
{
//一维数组
int array_1[3];
array_1[0] = 2;
array_1[1] = 3;
array_1[2] = 4;
int array_2[3] = {1,2,3};
int array_3[10] = {8,4,3,7,0,2, 1,2,3,4};
Print(array_3,sizeof(array_3)/sizeof(int));
Insertion_Sort(array_3, sizeof(array_3) / sizeof(int));
Print(array_3, sizeof(array_3) / sizeof(int));
//二维数组 必须指定内部维度数组的项数
int array_4[][3] = { {1,2},{2,3},{3,4} ,{3,4,3} };
}
/*
数组打印功能
*/
void Print(int* array,int length)
{
for (int i = 0; i < length; i++)
{
std::cout << array[i] << " ";
}
std::cout << std::endl;
}
/*
冒泡排序
*/
void Bubble_Sort(int* array, int length)
{
for (int i = 0; i < length; i++)
{
for (int j = 0; j < length-1; j++)
{
if (array[j] < array[j + 1])
{
Swap(array, j, j + 1);
}
}
}
}
/*
选择排序
*/
void Selection_Sort(int* array, int len)
{
for (int i = 0; i < len-1; i++)
{
int swap_index = i;
for (int j = i+1; j < len; j++)
{
if (array[swap_index] > array[j])
{
swap_index = j;
}
}
if (i != swap_index)
{
Swap(array, i, swap_index);
}
}
}
/*
插入排序
*/
void Insertion_Sort(int* array, int length)
{
for (int i = 0; i < length; i++)
{
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] > key)
{
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
}
/*
交换数组两个元素的位置
*/
void Swap(int* array, int index_f, int index_e)
{
int cache_number = array[index_f];
array[index_f] = array[index_e];
array[index_e] = cache_number;
}
案例:一维数组元素逆置
#include
void Swap(int* array, int index_f, int index_e);
void Print(int* array, int len);
int main()
{
//案例 一维数组元素逆置
int array[] = { 1,2,3,4,5,6,7,8,9,10,11,12};
int len = sizeof(array) / sizeof(array[0]);
Print(array, len);
for (int i = 0; i < len; i++)
{
if (i >= len - i -1)
{
break;
}
Swap(array, i, len - i -1);
}
Print(array, len);
}
void Swap(int* array, int index_f, int index_e)
{
std::cout << "swap" << std::endl;
int cache_number = array[index_f];
array[index_f] = array[index_e];
array[index_e] = cache_number;
}
void Print(int* array,int len)
{
for (int i = 0; i <len; i++)
{
std::cout << array[i] << " ";
}
std::cout << std::endl;
}
指针的作用:可以通过指针间接访问内存
#include
using namespace std;
int main()
{
int a = 10;
int* pointer_a = &a;
std::cout << &a << std::endl;
std::cout << "变量a的地址:" << pointer_a << " 指针pointer_a记录的地址的值为:" << *pointer_a << std::endl;
std::cout << "指针pointer_a占用的内存:" << sizeof(pointer_a) << std::endl;
}
指针所占的内存大小跟操作系统位数有关:32位操作系统占用4字节,64位操作系统占用8字节
空指针:
指针变量指向内存中的编号为0的空间,用于初始化指针变量,空指针指向的内存是不可以访问到
#include
using namespace std;
int main()
{
int* pointer = 0;
//int* pointer = nullptr;
//int* pointer = NULL;
std::cout << pointer << std::endl;
//运行下面这行代码时,会报错,因为空指针指向的内存是不可以访问的
//std::cout << *pointer << std::endl;
}
野指针:指针指向非法的内存空间
const修饰指针:
指针和数组
#include
using namespace std;
int main()
{
int array[] = { 1,2,3,4,5,6,7,8,9 };
int* pointer_array = array;
for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
{
std::cout << pointer_array << " ";
pointer_array++;
}
}
指针和函数
值传递(形参会改变,实参不会改变)和地址传递(形参和实参均改变)
#include
#include
int main()
{
//随机数种子
srand((unsigned int)time(NULL));
//循环生成随机数
for (int i = 0; i < 10; i++)
{
//范围 [40,99]
std::cout << rand() % 60 + 40 << " ";
}
}
C++面向对象的三大特性:封装、继承、多态
语法:
class 类名 { 访问权限:属性 / 行为 };
案例:设计一个圆类,求取圆的周长和面积
#include
#define PI 3.14
class Circle
{
private:
float radius;
public:
//设定圆的半径
void set_radius(float radius)
{
this->radius = radius;
}
//求取圆的周长
float round()
{
return 2 * this->radius * PI;
}
//求取圆的面积
float area()
{
return this->radius * this->radius * PI;
}
};
int main()
{
Circle circle;
circle.set_radius(12);
std::cout << "圆的周长:" << circle.round() << std::endl;
std::cout << "圆的面积:" << circle.area() << std::endl;
}
继承可以减少代码量
class A : public B
A类称之为子类或派生类
B类称之为父类或基类
派生类中的成员包含两部分:
继承的语法:
class 子类 : 继承方式 class 父类
继承方式:
#include
#include
using namespace std;
class Parent
{
private:
string password;
public:
string name;
protected:
string money;
};
class Children_1 : public Parent
{
public:
void printf();
};
class Children_2 : protected Parent
{
public:
void printf();
};
class Children_3 : private Parent
{
public:
void printf();
};
void Children_1::printf()
{
std::cout << this->name << std::endl;
std::cout << this->money << std::endl;
}
void Children_2::printf()
{
std::cout << this->name << std::endl;
std::cout << this->money << std::endl;
}
void Children_3::printf()
{
std::cout << this->name << std::endl;
std::cout << this->money << std::endl;
}
void printf()
{
Children_1 children;
children.name;
Children_1 children_2;
children_2.name;
Children_3 children_3;
}
#include
#include
using namespace std;
class Parent
{
private:
int name;
public:
int gender;
public:
//Parent()
//{
// std::cout << "父类的构造函数执行" << std::endl;
//}
//~Parent()
//{
// std::cout << "父类的析构函数执行" << std::endl;
//}
};
class Children : public Parent
{
public:
};
int main()
{
Children chi;
std::cout << "sizeof chi:" << sizeof(chi) << std::endl;
}
私有成员只是被隐藏了,但是还是会继承下去
父类中所有非静态的成员都会被继承下去,包括同名的成员
查看类的分布情况:
c1 /d1 reportSingleClassLayout + 类名 类所在的文件名
需要打开vs编辑器中开发者提示工具 Developer Command Prompt for VS 2022
cl /d1 reportSingleClassLayoutChildren ConsoleApplication1.cpp
#include
#include
using namespace std;
class Parent
{
public:
Parent()
{
std::cout << "父类的构造函数执行" << std::endl;
}
~Parent()
{
std::cout << "父类的析构函数执行" << std::endl;
}
};
class Children : public Parent
{
public:
Children()
{
std::cout << "子类的构造函数执行" << std::endl;
}
~Children()
{
std::cout << "子类的析构函数执行" << std::endl;
}
};
int main()
{
Children chi;
}
#include
#include
using namespace std;
class Parent
{
public:
int gender;
int name;
};
class Children : public Parent
{
private:
int name;
public:
};
int main()
{
Children chi;
//访问父类的同名属性,需要指定作用域
chi.Parent::name = 123;
}
成员函数同上调用方式!
如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉所有的父类同名的成员函数
C++允许一个类继承多个类,多个类之间使用逗号隔开
语法:class 子类 : 继承方式 父类1, 继承方式 父类2 …
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议使用多继承
两个派生类继承同一个基类
又有个派生类同时继承两个派生类
这种继承方式称为菱形继承,又称为钻石继承
vbptr 指向虚基类表
#include
#include
using namespace std;
class Parent
{
public:
int gender;
int name;
};
class Children :virtual public Parent
{
};
class Children_2 :virtual public Parent
{
};
class Grandson : public Children, public Children_2
{
};
int main()
{
Grandson gd;
//会存在两份同样的数据,需要加以作用域区分
//gd.name = 12;
//菱形继承导致了数据有两份,资源浪费
//利用虚继承可以解决菱形继承的问题
//在继承前加上关键字 virtual 变成虚继承
gd.name = 12;
}
多态分为两类:
静态多态的动态多态的区别:
#include
#include
using namespace std;
class Animal
{
public:
//虚函数
virtual void speak()
{
std::cout << "Animal speak" << std::endl;
}
};
class Cat : public Animal
{
public:
void speak()
{
std::cout << "Cat speak" << std::endl;
}
};
//地址早绑定,在编译阶段就已经确定了地址
//如果想执行Cat speak,需要地址晚绑定
void demo(Animal& animal)
{
animal.speak();
}
int main()
{
Cat cat;
demo(cat);
}
通过关键字virtual ,方法的地址将实现晚绑定,实现子类的动作行为实现
虚函数子类重写方法可加virtual 关键字也可不加,不影响最终结果
动态多态的满足条件:
虚函数表指针:
父类的虚函数会创建一个虚函数指针,指向虚函数表,表内会记录一个虚函数地址
当子类重写父类的虚函数,子类中的虚函数表内部会替换成子类的虚函数地址
当父类的指针或引用指向子类对象时候,发生多态
当子类Cat没有重写父类的虚函数时:
当子类Cat重写了父类的虚函数时:
案例:使用多态实现一个模拟的计算器类
#include
#include
using namespace std;
//计算器类 - 多态实现
class AbstractCalculator
{
public:
int num_1;
int num_2;
public:
virtual int getResult()
{
return 0;
}
};
class AddCalculator : public AbstractCalculator
{
public:
int getResult()
{
return this->num_1 + this->num_2;
}
};
class SubstructCalculator : public AbstractCalculator
{
public:
int getResult()
{
return this->num_1 - this->num_2;
}
};
int main()
{
AbstractCalculator* calculator = new AddCalculator();
calculator->num_1 = 10;
calculator->num_2 = 20;
std::cout << "Calculator result = " << calculator->getResult() << std::endl;
AbstractCalculator* subcCalculator = new SubstructCalculator();
subcCalculator->num_1 = 10;
subcCalculator->num_2 = 20;
std::cout << "Calculator result = " << subcCalculator->getResult() << std::endl;
}
在多态中,通常父类中的虚函数实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为传虚函数
class AbstractCalculator
{
public:
int num_1;
int num_2;
public:
//纯虚函数
//只要类中有一个纯虚函数,那么这个类就是一个抽象类
virtual int getResult() = 0;
};
抽象类:
#include
#include
using namespace std;
//计算器类 - 多态实现
class AbstractDrink
{
public:
virtual void Boid() = 0;
virtual void Brew() = 0;
virtual void PourInCub() = 0;
virtual void PutSomething() = 0;
void DoDrink()
{
Boid();
Brew();
PourInCub();
PutSomething();
}
};
class TeaDrink : public AbstractDrink
{
public:
void Boid()
{
std::cout << "煮茶水" << std::endl;
}
void Brew() {
std::cout << "冲泡茶叶" << std::endl;
}
void PourInCub()
{
std::cout << "导入茶杯中" << std::endl;
}
void PutSomething()
{
std::cout << "加柠檬" << std::endl;
}
};
class CoffeeDrink : public AbstractDrink
{
void Boid()
{
std::cout << "煮开水" << std::endl;
}
void Brew() {
std::cout << "冲泡咖啡" << std::endl;
}
void PourInCub()
{
std::cout << "导入咖啡杯中" << std::endl;
}
void PutSomething()
{
std::cout << "加糖和牛奶" << std::endl;
}
};
int main()
{
std::cout << "制作茶:" << std::endl;
AbstractDrink* drink = new TeaDrink;
drink->DoDrink();
//堆区的内存需要程序员手动释放
delete drink;
std::cout << "制作咖啡:" << std::endl;
//释放堆区内存后,drink指针的指引还在,可以再赋值
drink = new CoffeeDrink;
drink->DoDrink();
delete drink;
}
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父亲中的析构函数改为虚析构或者纯虚析构
#include
#include
using namespace std;
//计算器类 - 多态实现
class AbstractDrink
{
public:
virtual void Boid() = 0;
void DoDrink()
{
Boid();
}
virtual ~AbstractDrink()
{
std::cout << "父类析构函数调用" << std::endl;
}
};
class TeaDrink : public AbstractDrink
{
public:
void Boid()
{
std::cout << "煮茶水" << std::endl;
}
~TeaDrink()
{
std::cout << "子类析构函数调用" << std::endl;
}
};
class CoffeeDrink : public AbstractDrink
{
void Boid()
{
std::cout << "煮开水" << std::endl;
}
~CoffeeDrink()
{
std::cout << "子类析构函数调用" << std::endl;
}
};
int main()
{
std::cout << "制作茶:" << std::endl;
AbstractDrink* drink = new TeaDrink;
drink->DoDrink();
//堆区的内存需要程序员手动释放
delete drink;
std::cout << "制作咖啡:" << std::endl;
//释放堆区内存后,drink指针的指引还在,可以再赋值
drink = new CoffeeDrink;
drink->DoDrink();
delete drink;
}
虚析构和纯虚析构共性:
虚析构和纯虚析构区别:如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:virtual ~类名(){}
纯虚析构语法:virtaul ~类名() = 0; 类名::~类名(){}
案例:实现一个计算机的运作过程,展示不同的设备与厂家之间的关系
#include
#include
using namespace std;
class Cpu
{
public:
virtual void work() = 0;
};
class Gpu
{
public:
virtual void work() = 0;
};
class Memory
{
public:
virtual void work() = 0;
};
class Computer
{
public:
Cpu* cpu;
Gpu* gpu;
Memory* memory;
public:
void Work()
{
gpu->work();
cpu->work();
memory->work();
}
};
class IntelCpu : public Cpu
{
public:
IntelCpu()
{
}
void work()
{
std::cout << "intel cpu work!" << std::endl;
}
};
class IntelGpu : public Gpu
{
public:
IntelGpu()
{
}
void work()
{
std::cout << "intel Gpu work!" << std::endl;
}
};
class IntelMemory : public Memory
{
public:
IntelMemory()
{
}
void work()
{
std::cout << "intel Memory work!" << std::endl;
}
};
class LenoveCpu : public Cpu
{
public:
void work()
{
std::cout << "Lenove cpu work!" << std::endl;
}
};
class LenoveGpu : public Gpu
{
public:
void work()
{
std::cout << "Lenove Gpu work!" << std::endl;
}
};
class LenoveMemory : public Memory
{
public:
void work()
{
std::cout << "Lenove Memory work!" << std::endl;
}
};
int main()
{
Computer c1;
c1.cpu = new IntelCpu;
c1.gpu = new IntelGpu;
c1.memory = new IntelMemory;
c1.Work();
}
静态成员就是在成员变量和成员函数前加上关键字static,成为静态成员
静态成员分为:
#include
#include "string"
using namespace std;
class StaticClass
{
public :
static string static_str;
private:
string name;
public:
static void printStaticStr()
{
std::cout << "static str = " << static_str << std::endl;
}
};
//类内声明,类外初始化
string StaticClass::static_str = "111";
int main()
{
StaticClass staticStr;
staticStr.printStaticStr();
staticStr.static_str = "222";
staticStr.printStaticStr();
StaticClass staticStr2;
//所有对象共享同一份数据
staticStr2.printStaticStr();
//通过类名访问静态变量
std::cout << "static str = " << StaticClass::static_str << std::endl;
}
静态成员变量也是有访问权限的
#include
#include "string"
using namespace std;
class StaticClass
{
public :
static string static_str;
private:
string name;
public:
static void printStaticStr()
{
std::cout << "static str = " << static_str << std::endl;
}
};
//类内声明,类外初始化
string StaticClass::static_str = "111";
int main()
{
//通过类名调用
StaticClass::printStaticStr();
//通过类实例调用
StaticClass staticClass;
staticClass.printStaticStr();
}
空对象占用内存为1个字节
C++编译器会为每个空对象也分配一个字节的空间,是为了区分空对象占用内存的位置
每个空对象也应该有一个独一无二的内存地址
#include
#include "string"
using namespace std;
class Circle
{
};
int main()
{
Circle circle;
std::cout << "size of Circle = " << sizeof(circle) << std::endl;
}
非静态成员变量内存属于类的对象上
静态成员变量内存不属于类的对象上
成员函数内存不属于类的对象上
#include
#include "string"
using namespace std;
class Circle
{
public:
int m_A;
static int s_A;
public:
void print()
{
}
};
int main()
{
Circle circle;
std::cout << "size of Circle = " << sizeof(circle) << std::endl;
}
每一个非静态成员函数只会诞生一份函数实例,也就是多个同类型的对象会共用一块代码
C++通过提供特殊的对象指针,this指针,解决是哪个对象调用成员函数的
this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
#include
#include "string"
using namespace std;
class Circle
{
public:
int m_A;
static int s_A;
public:
void print(int m_A)
{
this->m_A = m_A;
}
};
#include
#include "string"
using namespace std;
class Circle
{
public:
int m_A;
static int s_A;
public:
Circle print(int m_A)
{
this->m_A = m_A;
return *this;
}
};
空指针可以访问成员函数,但是当访问到的成员函数有调用this时,将会出现异常
在成员函数中进行判空的操作可以加强程序的健壮性
常函数:
常对象:
#include
#include "string"
using namespace std;
class Circle
{
public:
//成员变量使用 mutable 修饰后,成员函数可以修改该值
mutable int m_A;
static int s_A;
public:
Circle()
{
this->m_A = 12;
}
//常函数 成员函数后加const
Circle print(int m_A) const
{
this->m_A = m_A;
return *this;
}
void print()
{
}
};
int main()
{
Circle circle;
circle.print(13);
std::cout << circle.m_A << std::endl;
const Circle circle_const;
//const修饰的类实例只能调用常函数
//circle_const.print();
}
在程序中,有一些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类访问另一个类中私有成员
友元的三种实现:
#include
#include "string"
using namespace std;
//全局函数做友元
class Circle
{
//全局函数作为友元,将全局函数定义到类内
friend void print(Circle& circle);
public:
string name;
private:
string password;
public:
Circle()
{
this->name = "username";
this->password = "password";
}
};
void print(Circle& circle)
{
std::cout << "全局函数访问" << std::endl;
std::cout << circle.password << std::endl;
}
#include
#include "string"
using namespace std;
//类做友元
class Circle
{
friend class Rrectangle;
public:
string name;
private:
string password;
public:
Circle()
{
this->name = "username";
this->password = "password";
}
};
class Rrectangle
{
public:
void print(Circle& circle);
};
void Rrectangle::print(Circle& circle)
{
std::cout << "类函数访问" << std::endl;
std::cout << circle.password << std::endl;
}
int main()
{
Rrectangle r;
Circle circle;
r.print(circle);
}
#include
#include "string"
using namespace std;
class Rrectangle
{
public:
Rrectangle();
private:
Circle circle;
public:
void print();
};
//成员函数做友元
class Circle
{
friend void Rrectangle::print();
public:
string name;
private:
string password;
public:
Circle()
{
this->name = "username";
this->password = "password";
}
};
Rrectangle::Rrectangle()
{
Circle circle;
this->circle = circle;
}
void Rrectangle::print()
{
std::cout << "成员函数访问" << std::endl;
std::cout << this->circle.password << std::endl;
}
int main()
{
Rrectangle r;
r.print();
}
对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
实现两个自定义数据类型相加的运算
运算符重载也可以发生函数重载
#include
#include "string"
using namespace std;
//成员函数做友元
class Circle
{
private:
int c_A;
int c_B;
public:
Circle()
{
this->c_A = 12;
this->c_B = 13;
}
public:
//通过成员函数重载
/*Circle operator+(Circle& circle)
{
Circle temp_circle;
temp_circle.c_A = this->c_A + circle.c_A;
temp_circle.c_B = this->c_B + circle.c_A;
return temp_circle;
}*/
void print()
{
std::cout << this->c_A << std::endl;
std::cout << this->c_B << std::endl;
}
int getC_A()
{
return this->c_A;
}
int getC_B()
{
return this->c_B;
}
void setC_A(int c_a)
{
this->c_A = c_a;
}
void setC_B(int c_b)
{
this->c_B = c_b;
}
};
//通过全局函数重载
Circle operator+(Circle& circle, Circle& circle_2)
{
Circle temp_circle;
temp_circle.setC_A(circle_2.getC_A() + circle.getC_A());
temp_circle.setC_B(circle_2.getC_B() + circle.getC_B());
return temp_circle;
}
int main()
{
Circle a;
Circle b;
Circle c = b + a;
c.print();
}
//
//
//
//
//
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
语法:
struct 结构体名称 { 结构体成员列表 };
通过结构体创建变量的方式有三种:
#include
using namespace std;
struct Student
{
public:
const char* name;
int cno;
} stu3;
int main()
{
Student stu;
stu.cno = 123;
stu.name = "ssss";
Student stu_2 = { "stu_2",123 };
}
结构体数组:
#include
using namespace std;
struct Student
{
public:
const char* name;
int cno;
};
void Print(Student* stu)
{
std::cout << "name = " << stu->name << " cno = " << stu->cno << std::endl;
}
void Print(Student stu)
{
std::cout << "name = " << stu.name<< " cno = " << stu.cno << std::endl;
}
int main()
{
Student stu_array[3] = { {"张三",12},{"李四",13},{"王二",14} };
Print(stu_array[0]);
Print(&stu_array[0]);
}
结构体指针:
利用操作符 -> 可以通过结构体指针访问结构体属性
结构体嵌套:
#include
using namespace std;
struct Teacher
{
public:
const char* name;
int tno;
};
struct Student
{
public:
const char* name;
int cno;
Teacher teacher;
};
void Print(Student* stu)
{
std::cout << "name = " << stu->name << " cno = " << stu->cno << std::endl;
std::cout << "teacher name = " << stu->teacher.name<< "teacher cno = " << stu->teacher.tno << std::endl;
}
void Print(Student stu)
{
std::cout << "name = " << stu.name<< " cno = " << stu.cno << std::endl;
}
int main()
{
Student stu_array[3] = { {"张三",12,{"李老师",12}},{"李四",13},{"王二",14}};
Print(&stu_array[0]);
}
结构体中使用const关键字:
节省内存且防止修改结构体的内容信息
构造函数主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
#include
#define PI 3.14
class Circle
{
private:
float radius;
public:
Circle(float radius)
{
this->radius = radius;
}
//设定圆的半径
void set_radius(float radius)
{
this->radius = radius;
}
//求取圆的周长
float round()
{
return 2 * this->radius * PI;
}
//求取圆的面积
float area()
{
return this->radius * this->radius * PI;
}
};
int main()
{
Circle circle(12);
std::cout << "圆的周长:" << circle.round() << std::endl;
std::cout << "圆的面积:" << circle.area() << std::endl;
}
两种分类方式:
#include
class Circle
{
private:
float radius;
public:
Circle()
{
//默认构造函数,不写该函数,编译器会提供一个
std::cout << "无参构造函数调用" << std::endl;
}
Circle(float radius)
{
this->radius = radius;
std::cout << "有参构造函数调用" << std::endl;
}
//拷贝构造函数 按照引用传参模式传递参数,且不能修改原本用于拷贝的类
Circle(const Circle& circle)
{
this->radius = circle.radius;
}
~Circle()
{
std::cout << "析构函数调用" << std::endl;
}
};
三种调用方式:
int main()
{
//括号法调用
Circle circle_default; //默认构造
Circle circle(12); //有参构造
Circle circle_copy(circle); //拷贝构造
//显示法调用
Circle circle_2 = Circle(12);
Circle(12); //匿名对象 当前行执行结束后,系统会立即回收掉匿名对象
//隐式法调用
Circle circle_3 = 10;
Circle circle_4 = { 10 };
Circle circle_5 = circle_3; //显示法调用拷贝构造
}
注意事项:
#include
#define PI 3.14
class Circle
{
private:
float radius;
public:
Circle()
{
this->radius = 12;
}
//拷贝构造函数
Circle(const Circle& circle)
{
this->radius = circle.radius;
std::cout << "拷贝构造函数调用" << std::endl;
}
};
void do_work(Circle circle)
{
}
Circle return_c()
{
Circle circle;
std::cout << &circle << std::endl;
return circle;
}
int main()
{
Circle circle;
//使用一个已经创建完毕的对象来初始化一个新对象
Circle circle_copy(circle);
//值传递方式给函数参数传值
do_work(circle);
//值方式返回局部对象
Circle c1 = return_c();
std::cout << &c1 << std::endl;
}
C++创建一个类,编译会给每个类至少添加三个函数
如果写了构造函数编译器将不再提供默认构造函数,但会提供拷贝构造函数;如果提供了拷贝构造,编译器将不再提供其它构造函数
浅拷贝:简单的赋值拷贝操作,浅拷贝带来的问题就是堆区的内存重复释放
深拷贝:在堆区重新申请空间,进行拷贝操作
#include
using namespace std;
class Circle
{
private:
float radius;
int* m_name;
public:
Circle()
{
this->radius = 12;
std::cout << "无参构造函数调用" << std::endl;
}
Circle(float radius,int* name)
{
this->radius = radius;
this->m_name = name;
std::cout << "有参构造函数调用" << std::endl;
}
//拷贝构造函数
Circle(const Circle& circle)
{
this->radius = circle.radius;
this->m_name = new int(*circle.m_name);
std::cout << "拷贝构造函数调用" << std::endl;
}
~Circle()
{
if (this->m_name != NULL)
{
delete this->m_name;
this->m_name = NULL;
}
std::cout << "析构函数调用" << std::endl;
}
void print()
{
std::cout << "radius = " << this->radius << "name = " << *this->m_name << std::endl;
}
};
void test_01()
{
int a = 123;
Circle* circle = new Circle(12.0, &a);
circle->print();
Circle circle_2(*circle);
circle_2.print();
}
int main()
{
test_01();
}
#include
using namespace std;
class Circle
{
private:
float radius;
int* m_name;
public:
Circle() :radius(10.2), m_name(new int(12))
{
}
Circle(float radius, int* m_name) :radius(radius), m_name(m_name)
{
}
float get_Radius()
{
return this->radius;
}
int* get_M_Name()
{
return this->m_name;
}
};
void test_01()
{
Circle circle;
std::cout << "radius = " << circle.get_Radius() << std::endl;
std::cout << "m_name = " << *circle.get_M_Name() << std::endl;
int a = 20;
Circle circle_2((float)20,&a);
std::cout << "circle_2 radius = " << circle_2.get_Radius() << std::endl;
std::cout << "circle_2 m_name = " << *circle_2.get_M_Name() << std::endl;
}
int main()
{
test_01();
}
B类中有A类的属性,其析构函数与构造函数执行顺序如下:
#include
using namespace std;
class A
{
public:
A()
{
cout << "A 类的默认构造调用" << endl;
}
~A()
{
cout << "A 类的析构函数调用" << endl;
}
};
class B
{
private:
A a;
public:
B()
{
cout << "B 类的默认构造调用" << endl;
}
~B()
{
cout << "B 类的析构函数调用" << endl;
}
};
int main()
{
B b;
}
主要作用于对象销毁前系统自动调用,执行一些清理工作
~Circle()
{
std::cout << "析构函数调用" << std::endl;
}
成员在类内可以访问,类外不可以访问
子类不可以访问父类的保护内容
成员在类内可以访问,内外也可以访问
成员在类内可以访问,内外不可以访问
子类可以访问父类的保护内容
操作文件的三大类:
读文件的基本步骤如下:
①包含头文件 #include
②创建流对象 ifstream ifs;
③打开文件并判断文件是否打开成功 ifs.open(“file uri”,打开方式);
④读数据 四种方式读取
⑤关闭文件 ifs.close()
写文件的基本步骤如下:
①包含头文件 #include
②创建流对象 ofstream ofs;
③打开文件 ofs.open(“file uri”);
④写数据 ofs << “写入的数据”;
⑤关闭文件 ofs.close();
文件打开方式:
打开文件方式可以配合使用,利用 | 操作符: ios::binary | ios::out
#include
#include
using namespace std;
int main()
{
//创建流对象
ofstream ofs;
//指定打开访问
ofs.open("C:/Users/25763/Desktop/test.txt", ios::out);
//写内容
ofs << "姓名 : 张三" << std::endl;
//关闭文件
ofs.close();
}
C++程序在执行时,将内存大方向划分为4个区域
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域:
#include
int q_a = 10;
int q_b = 10;
static int s_a = 10;
static int s_b = 10;
const int c_a = 10;
int main()
{
//全局区内存分配解析
//局部变量的内存不在全局区
int a = 10;
int b = 10;
std::cout << "局部变量 a 的地址:" << &a << std::endl;
std::cout << "局部变量 b 的地址:" << &b << std::endl;
//全局变量
std::cout << "全局变量 q_a 的地址:" << &q_a << std::endl;
std::cout << "全局变量 q_b 的地址:" << &q_b << std::endl;
//静态变量
static int s_c = 100;
static int s_d = 100;
std::cout << "全局静态变量 s_a 的地址:" << &s_a << std::endl;
std::cout << "全局静态变量 s_b 的地址:" << &s_b << std::endl;
std::cout << "局部静态变量 s_c 的地址:" << &s_c << std::endl;
std::cout << "局部静态变量 s_d 的地址:" << &s_d << std::endl;
//常量 --- 字符串常量 / const修饰的常量
std::cout << "字符串常量的地址:" << &"hello" << std::endl;
const int c_b = 10;
std::cout << "const修饰的全局常量 c_a 的地址:" << &c_a << std::endl;
std::cout << "const修饰的局部常量 c_b 的地址:" << &c_b << std::endl;
}
全局区包含有:全局变量、静态变量(static关键字修饰)、const修饰的全局常量、字符串常量
不在全局区的有:局部变量,const修饰的局部变量(局部常量)
有编译器自动分配释放,存放函数的参数值、局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
#include
int* func(int c = 10) //形参数据也会放在栈区
{
int a = 10;
//局部变量存放在栈区,执行结束后程序自动释放
return &a;
}
int main()
{
int* func_a = func();
//编译器做了保留,第一次可以正常访问该变量
std::cout << *func_a << std::endl;
//后续可能无法正常访问该变量
std::cout << *func_a << std::endl;
}
由程序员分配释放,若是程序员不释放,程序结束后由操作系统回收
在C++中主要利用 new 关键字在堆区开辟内存
#include
int* func()
{
int* a = new int(10);
return a;
}
int main()
{
int* func_a = func();
std::cout << *func_a << std::endl;
std::cout << *func_a << std::endl;
}
C++利用new操作符在堆区开辟内存
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete
语法:
new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针
#include
int main()
{
int* a = new int(10);
std::cout << *a << std::endl;
delete a;
//执行该行时,会报错,内存访问无权限
std::cout << *a << std::endl;
}
给变量起别名
数据类型& 别名 = 原名
#include
int main()
{
int a = 10;
int& b = a;
b = 11;
std::cout << a << std::endl;
}
#include
int main()
{
int a = 10;
int c = 11;
int& b = a;
//赋值操作,而不是更改引用
b = c;
b = 9;
std::cout << c << std::endl;
std::cout << a << std::endl;
}
函数传参时,可以利用引用的技术让形参修饰实参,可以简化指针修改实参
#include
//值传递
void swap(int a, int b);
//地址传递
void swap(int* a, int* b);
//引用传递
void swap_quote(int& a, int& b);
int main()
{
int a = 10;
int b = 20;
swap(a, b);
std::cout << "a=" << a << std::endl;
std::cout << "b=" << b << std::endl;
swap(&a, &b);
std::cout << "a=" << a << std::endl;
std::cout << "b=" << b << std::endl;
swap_quote(a, b);
std::cout << "a=" << a << std::endl;
std::cout << "b=" << b << std::endl;
}
//值传递
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
//地址传递
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//引用传递
void swap_quote(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
通过引用参数产生的效果和按地址传递的效果是一样的,引用的语法更清楚简单
注意:不要返回局部变量引用
#include
int& add(int a, int b);
int main()
{
int c = add(3, 4);
std::cout << c << std::endl;
std::cout << c << std::endl;
}
//返回局部变量的引用时,可能会出现无法访问的问题
int& add(int a, int b)
{
int c = a + b;
int& result = c;
return result;
}
函数的调用可以作为左值
#include
int& add(int a, int b);
int main()
{
int c = add(3, 4);
std::cout << c << std::endl;
std::cout << c << std::endl;
//函数引用的可以作为左值进行赋值操作
c = add(4, 5) = 900;
std::cout << c << std::endl;
std::cout << c << std::endl;
}
//返回局部变量的引用时,可能会出现无法访问的问题
int& add(int a, int b)
{
int c = a + b;
int& result = c;
return result;
}
引用的本质在C++内部实现是一个指针常量
int& a = b 等效为 int* const a = &b
常量引用主要是用来修饰形参,防止误操作
在函数形参列表中,可以添加const修饰形参,防止形参改变实参
通讯录是一个可以记录亲人、好友信息的工具,系统中需要实现的功能如下:
使用结构体记录联系人信息,在控制台中展示所有的操作流程
系统代码结构:
#pragma once
#include
#include "string"
using namespace std;
struct Address_Book;
//添加联系人方法
void Add();
//打印共有多少联系人方法
void Print_Info();
//显示通讯录所有联系人信息
void Show();
//显示目标Address_Book结构体中的联系人信息
void Show(Address_Book* address_book);
//查找联系人信息
void Find();
//根据联系人姓名查找联系人信息
int Find(string name);
//删除联系人方法
void Delete();
//清空联系人
void Clear();
//修改联系人信息
void Update();
//菜单展示
void Show_Menu();
#include
#include "string"
#include "ad_book.h"
using namespace std;
//联系人最大支持的数量定义
#define MAX_LEN 1000
//通讯录记录信息的结构体
struct Address_Book
{
//姓名
string name;
//性别
string gender;
//年龄
int age = 0;
//联系电话
string phone;
//家庭住址
string address;
};
//联系人记录结构
static Address_Book address_book_array[MAX_LEN];
//当前共有联系人数量
static int Now_Size = 0;
//添加联系人方法
void Add()
{
std::cout << "-------------------------------------------------------------------------------" << std::endl;
Address_Book address_book;
//打印输出添加各项信息的提示
std::cout << "请输入您要添加的联系人姓名:" << std::endl;
std::cin >> address_book.name;
std::cout << "请输入您要添加的联系人性别:" << std::endl;
std::cin >> address_book.gender;
std::cout << "请输入您要添加的联系人年龄:" << std::endl;
std::cin >> address_book.age;
std::cout << "请输入您要添加的联系人联系电话:" << std::endl;
std::cin >> address_book.phone;
std::cout << "请输入您要添加的联系人家庭住址:" << std::endl;
std::cin >> address_book.address;
address_book_array[Now_Size++] = address_book;
std::cout << "-------------------------------------------------------------------------------" << std::endl;
Print_Info();
}
//打印输出当前共有多少的联系人信息
void Print_Info()
{
std::cout << "-------------------------------------------------------------------------------" << std::endl;
std::cout << "当前共有:" << Now_Size << " 个联系人 " << std::endl;
std::cout << "-------------------------------------------------------------------------------" << std::endl;
}
//显示通讯录所有联系人信息
void Show()
{
std::cout << "-------------------------------------------------------------------------------" << std::endl;
for (int i = 0; i < Now_Size; i++)
{
Show(&(address_book_array[i]));
}
std::cout << "-------------------------------------------------------------------------------" << std::endl;
}
//显示目标Address_Book结构体中的联系人信息
void Show(Address_Book* address_book)
{
std::cout << "姓名: " << address_book->name << " 性别: " << address_book->gender << " 年龄: "
<< address_book->age << " 联系电话: " << address_book->phone << "家庭住址: " << address_book->address << std::endl;
}
//查找联系人信息
void Find()
{
std::cout << "-------------------------------------------------------------------------------" << std::endl;
std::cout << "请输入您要查找的目标联系人姓名:" << std::endl;
string wait_find_name;
std::cin >> wait_find_name;
Find(wait_find_name);
std::cout << "-------------------------------------------------------------------------------" << std::endl;
}
//根据联系人姓名查找联系人信息
int Find(string name)
{
for (int i = 0; i < Now_Size; i++)
{
if (address_book_array[i].name == name)
{
Show(&address_book_array[i]);
return i;
}
}
std::cout << "用户姓名:" << name << " 对应的联系人信息未查找到!" << std::endl;
return -1;
}
//删除联系人方法
void Delete()
{
std::cout << "-------------------------------------------------------------------------------" << std::endl;
std::cout << "请输入您要删除的目标联系人姓名:" << std::endl;
string wait_delete_name;
std::cin >> wait_delete_name;
int wait_delete_index = Find(wait_delete_name);
if (wait_delete_index >= 0)
{
//把后面的内容交换到前面
for (int i = wait_delete_index; i < Now_Size; i++)
{
address_book_array[i] = address_book_array[i + 1];
}
}
Now_Size--;
std::cout << "-------------------------------------------------------------------------------" << std::endl;
}
//清空联系人
void Clear()
{
*address_book_array = {};
Now_Size = 0;
std::cout << "联系人清空完毕!" << std::endl;
}
//修改联系人信息
void Update()
{
std::cout << "-------------------------------------------------------------------------------" << std::endl;
std::cout << "请输入您要修改的目标联系人姓名:" << std::endl;
string wait_update_name;
std::cin >> wait_update_name;
int wait_update_index = Find(wait_update_name);
if (wait_update_index >= 0)
{
Address_Book address_book;
std::cout << "请输入您要修改的联系人姓名:" << std::endl;
std::cin >> address_book.name;
std::cout << "请输入您要修改的联系人性别:" << std::endl;
std::cin >> address_book.gender;
std::cout << "请输入您要修改的联系人年龄:" << std::endl;
std::cin >> address_book.age;
std::cout << "请输入您要修改加的联系人联系电话:" << std::endl;
std::cin >> address_book.phone;
std::cout << "请输入您要修改的联系人家庭住址:" << std::endl;
std::cin >> address_book.address;
address_book_array[wait_update_index] = address_book;
}
std::cout << "-------------------------------------------------------------------------------" << std::endl;
}
//菜单展示
void Show_Menu()
{
while (true)
{
std::cout << "-------------------------------------------------------------------------------" << std::endl;
std::cout << "欢迎来到通讯录管理系统,当前通讯录共有 " << Now_Size << " 个联系人!" << std::endl;
std::cout << "请选择您当前要进行的操作:" << std::endl;
std::cout << "A:添加联系人" << std::endl;
std::cout << "S:显示所有联系人" << std::endl;
std::cout << "D:删除联系人" << std::endl;
std::cout << "F:查找联系人" << std::endl;
std::cout << "U:修改联系人" << std::endl;
std::cout << "C:清空联系人" << std::endl;
std::cout << "E:退出当前系统" << std::endl;
char input;
std::cin >> input;
switch (input)
{
case 'A': Add(); break;
case 'S': Show(); break;
case 'D': Delete(); break;
case 'F': Find(); break;
case 'U': Update(); break;
case 'C': Clear(); break;
case 'E': exit(0);
}
std::cout << "-------------------------------------------------------------------------------" << std::endl;
}
}
#include
#include
#include "ad_book.h"
int main()
{
Show_Menu();
}