C++语言常见知识点
C++基础
变量存在的意义是什么?
作用:给一段指定的内存空间起名,方便我们管理操作这块内存。
int a=10;
常量的两种定义方式
1、#define PI 3.14 2、const int a=10;
用于记录程序中不可更改的数据。
标识符的命名规则
1、不能是关键字
2、只能由字母、下划线、数字构成
3、不能数字开头
4、区分大小写
为什么要有数据类型?
C++规定在一个变量或者常量时,必须要指出相应的数据类型,否则无法给变量分配内存。
存在的意义:给变量分配合适的内存空间,不要造成浪费。
sizeof关键字有什么用?
利用sizeof关键字可以统计数据类型所占用内存大小(字节大小)。
sizeof( 数据类型/变量 )
科学计数法表示
float f=3e2;// 3* (10^2)
float f2=3e-2;// 3* (0.1^2)
C++中的字符变量
char a='a';
- C/C++中字符型变量只占用1个字节。
- 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII码放入到存储单元。
- cout<<(int)a<<endl; a=>97;A=>65
转义字符:用于表示一些不能显示出来的ASCII字符。 \n \\ \t
C++生成随机数
int num = rand() % 100; // 0~99
goto语句
不推荐用,知道就好。
cout << "a" << endl;
cout << "b" << endl;
goto FLAG;
cout << "c" << endl;
FLAG:
cout << "d" << endl;
/*
a
b
d
*/
数组
所谓数组,就是一个集合,里面存放了相同类型的数据元素。
特点:1、每个数据元素都是相同的数据类型2、数组由连续的内存地址组成
一维数组名的用途
int arr[5]={1,2,3,4,5};
1、可以统计整个数组在内存中的长度
sizeof(arr)、sizeof(arr[0])
2、获取数组在内存中的首地址。
cout<<arr<<endl;
cout<<&arr[0]<<endl; // 获取数组中第一个元素地址。与数组首地址一样
二维数组名的用途
int arr[2] [3]={{1,2,3},{4,5,6}};
1、可以统计整个数组在内存中的长度
sizeof(arr)、sizeof(arr[0]) 第一行所占用的内存空间大小、sizeof(arr[0] [0]) 第一个元素所占内存空间大小
sizeof(arr)/sizeof(arr[0]) 获取行数、sizeof(arr[0])/sizeof(arr[0] [0]) 获取列数
2、获取数组在内存中的首地址。
cout<<arr<<endl;
cout<<arr[0]<<endl; 获取第一行数据首地址
cout<<arr[1]<<endl;
函数有什么用?
作用:将一段经常使用的代码封装起来,减少重复代码。
一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能。
什么是函数值传递?
所谓值传递,就是函数调用时实参将数传入给形参。
值传递时,如果形参发生改变,并不影响实参。
void swap(int a, int b)
{
cout << "2:a= " << a << endl;
cout << "2:b= " << b << endl;
int temp = a;
a = b;
b = temp;
cout << "2:a= " << a << endl;
cout << "2:b= " << b << endl;
}
int main()
{
int a = 10;
int b = 20;
cout << "1:a= " << a << endl;
cout << "1:b= " << b << endl;
swap(a, b);
cout << "1:a= " << a << endl;
cout << "1:b= " << b << endl;
system("pause");
return 0;
}
/*
1:a= 10
1:b= 20
2:a= 10
2:b= 20
2:a= 20
2:b= 10
1:a= 10
1:b= 20
*/
指针有什么用?
作用:可以通过指针间接访问内存。
- 内存编号是从0开始记录的,一般用十六进制数字表示
- 可以利用指针变量来保存地址
int a = 10;
int* p = &a;// 让指针p记录a的地址
cout << "a= " << a << endl;
cout << "*p= " << *p << endl;// 解引用
cout << "&a= " << &a << endl;// &取地址
cout << "p= " << p << endl;
/*
a= 10
*p= 10
&a= 000000A2FEEFF5C4
p= 000000A2FEEFF5C4
*/
指针这种数据类型所占用的内存空间有多大呢?
sizeof(int *)
32位操作系统,占用4个字节
64位操作系统,占用8个字节
什么是空指针?有什么用?什么是野指针?
空指针:指针变量指向内存中编号为0的空间。
用途:初始化指针变量 int * p=NULL;
注意:空指针指向的内存不可以访问(0~255之间的内存编号是系统占用的,因此不可访问)
野指针:指针变量指向非法的内存空间。程序中,避免出现野指针。
const修饰指针有哪些情况?有什么区别?
1、const修饰指针 --- 常量指针
const int * p=&a; 指针指向可以修改,但指针指向的值不可以改。*p=20(X)p=&b(V)
2、const修饰常量 --- 指针常量
int * const p=&a; 指针指向不可以修改,但指针指向的值可以改。*p=20(V)p=&b(X)
【记法:const 和 * 谁在前,对应的就不能变。例如const在前,常量值就不能变;*在前,指针的指向不能变】
3、const既修饰指针又修饰常量
const int * const p=&a; 指针指向不可以修改,但指针指向的值不可以改。*p=20(X)p=&b(X)
int m = 10;
int n = 10;
const int* pt1 = &m;// 常量指针
pt1 = &n;
//*pt1 = 20;// 报错
int* const pt2 = &m;// 指针常量
//pt2 = &n;// 报错
*pt2 = 20;
const int* const pt3 = &m;// const既修饰指针又修饰常量
//pt3 = &n;// 报错
//*pt3 = 20;// 报错
如何利用指针访问数组中的元素?
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
cout << arr[0] << endl;
int* ptr = arr;// arr就是arr数组的首地址
cout << ptr << endl;
ptr++;// ptr向后偏移4个字节
cout << ptr << endl;
int* ptr2 = arr;
for (int i = 0; i < 10; i++)
{
cout << *ptr2 << " ";
ptr2++;
}
指针作为函数参数会发生什么情况?
可以修改实参的值。
// 值传递
void swap(int a, int b)
{
int temp = b;
b = a;
a = temp;
cout << "内部a= " << a << endl;
cout << "内部b= " << b << endl;
}
// 引用传递
void swap2(int* a, int* b)
{
int temp = *b;
*b = *a;
*a = temp;
cout << "内部a= " << *a << endl;
cout << "内部b= " << *b << endl;
}
int a = 10;
int b = 20;
//swap(a, b); 10 20
swap2(&a, &b);20 10
cout << "a= " << a << endl;
cout << "b= " << b << endl;
示例:指针结合数组和函数
// 数组的冒泡排序
void bubbleSort(int* arr, int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
// 打印数组
void printArray(int* arr, int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
}
int main()
{
int arr[] = { 3,1,7,4,6,9,10,2,5,8 };
int len = sizeof(arr) / sizeof(arr[0]);// 数组长度
cout << "排序前:";
printArray(arr, len);// 打印数组
cout << endl;
bubbleSort(arr, len);// 给数组排序
cout << "排序后:";
printArray(arr, len);// 打印数组
cout << endl;
system("pause");
return 0;
}
什么是结构体?
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
struct Student
{
string name;
int age;
float score;
};
struct Teacher
{
int id;
string name;
int age;
struct Student stu;// 结构体嵌套
};
// 结构体作值传递
void PrintStudent(struct Student s)
{
s.age = 100;
cout << s.name << " " << s.age << " " << s.score << endl;
}
// 结构体作引用传递:可以减少空间,不会复制新的副本出来
void PrintStudent2(struct Student* s)
{
s->age = 200;
cout << s->name << " " << s->age << " " << s->score << endl;
}
// 结构体作引用传递:可以减少空间,不会复制新的副本出来。
// 防止误操作,加const,只能读不能写
void PrintStudent2(const struct Student* s)
{
cout << s->name << " " << s->age << " " << s->score << endl;
}
int main()
{
struct Student s1;
s1.name="张三";
s1.age=21;
s1.score=100;
Student s2={"李四",20,80};
cout<<s1.name<<" "<<s1.age<<" "<<s1.score<<endl;
cout<<s2.name<<" "<<s2.age<<" "<<s2.score<<endl;
// 结构体数组
Student stus[2] = { s1,s2 };
stus[0].name="张飞";
// 结构体指针
Student * p=&s1;
cout<< p->name << " " << p->age << " " << p->score;
// 嵌套结构体
Teacher t;
t.id=1;
t.name="老王";
t.age=40;
t.stu=s1;
// 结构体作函数参数
cout << "------------------" << endl;
PrintStudent(s2);
cout << s2.name << " " << s2.age << " " << s2.score << endl;
PrintStudent2(&s2);
cout << s2.name << " " << s2.age << " " << s2.score << endl;
system("pause");
return 0;
}
C++核心
C++的内存分区有哪些?为什么要分区?
程序运行前:
1、代码区(存放函数体的二进制代码【共享、只读】,由操作系统进行管理)
2、全局区(存放全局变量和静态变量以及常量【字符串常量和const修饰的全局常量】,程序结束时由操作系统释放)
程序运行后:
3、栈区(由编译器自动分配释放,存放函数的参数值,局部变量等)
4、堆区(由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。主要利用new在堆区开辟内存;delete释放)
int * p = new int(10);//*p 存放在栈区,new int(10)存放在堆区
cout<< *P <<endl;//10
delete p;
// new、delete开辟数组
int * arr= new int[10];
for(int i=0;i<10;i++)
{
arr[i]=i+100;
}
for(int i=0;i<10;i++)
{
cout<<arr[i]<<endl;//100~109
}
delete []arr;
分区存在的意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。
C++中的引用有什么用?
给变量起别名。 数据类型 & 别名 = 原名
注意:1、引用必须初始化 2、一旦初始化之后,就不可以更改了。
使用引用作函数参数的优点是什么?
函数传参时,可以利用引用的技术让形参修饰实参。可以简化指针修改实参。
void swap(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int m = 10;
int n = 20;
cout << m << endl;//10
cout << n << endl;//20
swap(m, n);
cout << m << endl;//20
cout << n << endl;//10
system("pause");
return 0;
}
引用的本质是什么?
引用的本质在c++内部实现是一个指针常量(指针的指向不能改变,值可以改变)。
为什么会有常量引用?
常量引用主要用来修饰形参,防止误操作。 const int& a=10; //(X)a=20;
面向对象的三大特性是什么?什么是类?
封装、继承、多态。
具有相同性质的对象,我们可以抽象为类。
封装有什么意义?
1、将属性和行为作为一个整体,变现生活中的事物。
2、将属性和行为加以权限控制。A. public公共权限 B. protected 保护权限(子可以访问父) C. private 私有权限
结构体和类的区别是什么?
c++中,struct和class唯一的区别就在于 默认的访问权限不同。struct 公共;class private
成员属性设置成私有的好处是什么?
1、可以控制自己读写权限( get,set方法) 2、对于 写,可以检测数据的有效性 (set方法内条件判断)
构造函数和析构函数的作用是什么?
构造函数:对象的初始化,无需手动调用,只会调用一次
析构函数:在对象销毁前系统自动调用,执行一些清理工作,只会调用一次【在堆区开辟的数据释放掉】
构造函数的调用规则
-
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
-
如果用户定义拷贝构造函数,c++不会再提供其他构造函数
深拷贝和浅拷贝是什么?有什么区别?
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
区别:
浅拷贝的问题:
深拷贝解决:
B类中有对象A作为成员,A为对象成员。那么创建B对象时,A与B的构造和析构的顺序是谁先谁后?
A的构造——>B的构造——>B的析构——>A的析构
C++静态成员的注意事项
包括:
静态成员变量:1、所有对象共享同一份数据 2、在编译阶段分配内存(全局区)
3、类内声明,类外初始化 (注意访问权限)int Person::m_A=100; 访问:p.m_A; p1.m_A; Person::m_A;
静态成员函数:(注意访问权限)1、所有对象共享同一个函数 2、静态成员函数只能访问静态成员变量
static void func(){} 访问:p.func(); Person::func();
C++的成员变量和成员函数分开存储。
只有非静态成员变量属于类对象上。sizeof(p)
如果类什么都没有,空对象占1个字节;有非静态成员变量,按照对应变量的大小算
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用同一份代码。
this指针有什么用?
this指针指向被调用的成员函数所属的对象。本质是指针常量
- 形参和变量成员同名时,可用this指针来区分
- 在类的非static成员函数中返回对象本身,可使用return *this;
const修饰成员函数的用途是什么?
常函数:1、常函数内不能修改成员属性 2、成员属性声明时加关键字mutable后,在常函数中依然可以修改。
void showPerson() const { }
常对象:常对象只能调用常函数
友元的目的是什么?
让一个函数或者类 能够访问另一个类中的私有成员。friend
什么是运算符重载?
对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
使用继承有什么好处?
减少重复代码 class Java: public BasePage { }
继承方式:1、公共继承 2、保护继承 3、私有继承
父类中所有非静态成员属性都会被子类继承下去。sizeof(son) 包括父类的私有非静态属性
继承中父类和子类的构造和析构顺序谁先谁后?
Base构造——>Son构造——>Son析构——>Base析构
当子类和父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
-
访问子类同名成员,直接访问即可。 s.m_A; s.Func();
-
访问父类同名成员,需要加作用域。 s.Base::m_A; s.Base::Func();
静态成员同名情况下的处理方式一致。
菱形继承问题
使用 虚继承 解决菱形继承的问题。
哪些属于多态?
1、静态多态:函数重载 和 运算符重载属于静态多态,复用函数名。 编译阶段确定函数地址
2、动态多态:派生类和虚函数实现运行时多态。 运行阶段确定函数地址
#include <iostream>
using namespace std;
class Base
{
public:
virtual void speak()
{
cout << "Base" << endl;
}
};
class Son1 :public Base
{
public:
//重写Base方法
void speak()
{
cout << "Son1" << endl;
}
};
class Son2 :public Base
{
public:
//重写Base方法
void speak()
{
cout << "Son2" << endl;
}
};
void Speak(Base& base)//Base& base=son1
{
base.speak();
}
int main()
{
Son1 son1;
Speak(son1);//Son1
Son2 son2;
Speak(son2);//Son2
system("pause");
return 0;
}
多态的优点有哪些?
1、代码组织结构清晰 2、可读性强 3、利于前期和后期的扩展和维护
纯虚函数的由来?什么是抽象类,有什么特点?
在多态中,通常父类中的虚函数的实现是毫无意义的,主要都是调用子类重写的内容。
可将虚函数改成 纯虚函数。【virtual 返回值类型 speak() = 0;】
当类中有了纯虚函数,这个类也称为抽象类。
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类。
虚析构和纯虚析构的用处?
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。
——使用虚析构或者纯虚析构
virtual ~Base() { } | virtual ~Base() = 0;Base::~Base() { }
共性:1、可以解决父类指针释放子类对象 2、都需要具体的函数实现
区别:如果是纯虚析构,该类属于抽象类,无法实例化对象。
文件操作
头文件:
ofstream:写操作 ifstream:读操作 fstream:读写操作