文章目录
1 . sizeof
统计数据类型所占内存大小
2 . 整型常量
可以用3种不同的方式表示:十进制,八进制0,十六进制0x
3 . 局部变量
- 在一个函数内部定义的变量是局部变量,它只在本函数范围内有效
- 形参也是局部变量
4 .运算
-
运算的两个数中有一个数为float型数据,则运算的结果是double型,因为C++在运算时对所有float型数据都按double型数据处理。
-
只有整型变量可以进行取模运算,两个小数不可以取模。
-
取模运算时,除数也不能为0。
5.–++:
-
前置后置运算符单独使用没有什么区别
-
前置递增先对变量进行++,再计算表达式
-
后置递增先计算表达式,后对变量进行++
扫描二维码关注公众号,回复: 15260442 查看本文章
6 .字符串函数
- 连接 strcat ,复制 strcpy, 比较strcmp , 长度 strlen。
7 .函数
- 函数定义里小括号内称为形参,函数调用时传入的参数称为实参
- 函数不能嵌套定义但是可以嵌套调用(常考)
- 所谓值传递,即单向传递,就是函数调用时实参将数值传入给形参,而不能由形参传回来给实参。
- 值传递时,如果形参发生改变,并不会影响实参(值传递时,形参是修饰不了实参的),请务必理解并记住,此处因篇幅就不进行讲解了!
- 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
- 如果函数声明有默认值,函数实现的时候就不能有默认参数。
8 .函数重载:
-
函数名称相同
-
函数 参数 类型不同 或者 个数不同 或者 顺序不同
-
注意: 函数的返回值不可以作为函数重载的条件
-
C++中有以下五个运算符不能重加粗样式载
成员访问运算符 .
成员指针访问运算符 *
域运算符 ::
长度运算符 sizeof
条件运算符 ?: -
重载运算符规则:
重载 不能 改变运算符运算对象(即操作数)的个数
重载 不能 改变运算符的优先级别
重载 不能 改变运算符的结合性
运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数 -
一般将单目运算符重载为成员函数,将双目运算符(二元运算符)重载为友元函数
9 .数组
- 直接打印数组名,可以查看数组所占内存的首地址
- 对数组名进行sizeof加粗样式,可以获取整个数组占内存空间的大小
- 二维数组如果对全部元素赋初值,定义数组时对第一维的长度可以不指定,但是第二维的长度 不能 省略
- 只能对字符数组的元素赋值,而 不能 用赋值语句对整个数组赋值
10 .内联函数
规模较小而又被频繁调用的简单函数,才适合于声明为inline函数。
11 .模板
template< typename T>
函数声明或定义
- template — 声明创建模板
- typename — 表面其后面的符号是一种数据类型,可以用class代替
- T — 通用的数据类型,名称可以替换,通常为大写字母
只适用于函数体相同、函数的参数个数相同而类型不同的情况, 参数个数 不同不能用函数模板。
#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{
// 如果为空则返回真。
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem)
{
// 追加传入元素的副本
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
}
template <class T>
T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}
int main()
{
try {
Stack<int> intStack; // int 类型的栈
Stack<string> stringStack; // string 类型的栈
// 操作 int 类型的栈
intStack.push(7);
cout << intStack.top() <<endl;
// 操作 string 类型的栈
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
12 .指针
指针的定义
int a = 10;
int * p;
p = &a; //指针指向变量a的地址
cout << &a << endl; //打印数据a的地址
cout << p << endl; //打印指针变量p
指针的使用
//通过*操作指针变量指向的内存
cout << "*p = " << *p << endl;
// *p = 10
const修饰指针有三种情况:
int a = 10;
int b = 10;
//const修饰的是指针,指针指向可以改,指针指向的值不可以更改
const int * p1 = &a;
p1 = &b; //正确
//*p1 = 100; 报错
//const修饰的是常量,指针指向不可以改,指针指向的值可以更改
int * const p2 = &a;
//p2 = &b; //错误
*p2 = 100; //正确
//const既修饰指针又修饰常量
const int * const p3 = &a;
//p3 = &b; //错误
//*p3 = 100; //错误
看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量
指针和数组
int main() {
int arr[] = {
1,2,3,4,5,6,7,8,9,10 };
int * p = arr; //指向数组的指针
cout << "第一个元素: " << arr[0] << endl; //1
cout << "指针访问第一个元素: " << *p << endl; //1
for (int i = 0; i < 10; i++)
{
//利用指针遍历数组
cout << *p << endl;
p++;
}
指针和函数
不修改实参,值传递,改实参,就用地址传递
返回指针值的函数简称指针函数。
13 .引用
给变量起别名
数据类型 &别名 = 原名
引用很容易与指针混淆,它们之间有三个主要的不同:
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
- 引用必须在创建时被初始化。指针可以在任何时间被初始化。
引用作函数的参数和返回值
//1. 值传递
void mySwap01(int a, int b) {
int temp = a;
a = b;
b = temp;
}
//2. 地址传递
void mySwap02(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
//参数:把地址传进去,用指针接收
//3. 引用传递
void mySwap03(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
//参数:别名,下面的a是上面的a的别名,用别名操作修改可原名操作修改是一样的
int a = 10;
int b = 20;
// 值传递,形参不会修饰实参
mySwap01(a, b);
cout << "a:" << a << " b:" << b << endl;
// a:10 b:20
// 地址传递,形参会修饰实参
mySwap02(&a, &b);
cout << "a:" << a << " b:" << b << endl;
// a:20 b:10
// 引用传递,形参会修饰实参
mySwap03(a, b);
cout << "a:" << a << " b:" << b << endl;
// a:20 b:10
//数据类型后加&,相当于用引用的方式返回
int& test02() {
// 必须使用静态变量,需加 static 关键字
static int a = 20;
return a;
}
int main(){
int& ref2 = test02();
system("pause");
return 0;
}
14 .类和对象
-
struct和class唯一区别: 默认访问权限不同,struct 公共,class 私有。
-
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
-
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法:类名(){}(构造和析构很容易出选择题,特点要记住) -
构造函数,没有返回值也不写void
构造函数的名字必须与类名相同
构造函数 可以 有参数,因此可以发生重载
程序在调用对象时候会自动调用构造函数,无须手动调用,而且只会调用一次(构造函数不需用户调用,也不能被用户调用) -
析构函数语法: ~类名(){}
析构函数,没有返回值也不写void
函数名称与类名相同,在名称前加上符号 ~
析构函数 不可以 有参数,因此不可以发生重载
程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
-
尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时只执行其中一个构造函数,并非每个构造函数都被执行
15 .初始化列表语法
构造函数():属性1(值1),属性2(值2)… {}
16 .类对象作为类成员
那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后?
先调用对象成员的构造,再调用本类构造(如上例中,先调用A的构造函数)
析构顺序与构造相反
17 .静态成员:
- 变量
- 函数
class Person
{
public:
static int m_A; //静态成员变量
//静态成员变量特点:
//1 在编译阶段分配内存
//2 类内声明,类外初始化
//3 所有对象共享同一份数据
private:
static int m_B; //静态成员变量也是有访问权限的
};
int Person::m_A = 10;
int Person::m_B = 10;
void test01()
{
//静态成员变量两种访问方式
//1、通过对象
Person p1;
p1.m_A = 100;
cout << "p1.m_A = " << p1.m_A << endl;
Person p2;
p2.m_A = 200;
cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据
cout << "p2.m_A = " << p2.m_A << endl;
//2、通过类名
cout << "m_A = " << Person::m_A << endl;
//cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
}
int main() {
test01();
system("pause");
return 0;
}
class Person
{
public:
//静态成员函数特点:
//1 程序共享一个函数
//2 静态成员函数只能访问静态成员变量
static void func()
{
cout << "func调用" << endl;
m_A = 100;
//m_B = 100; //错误,不可以访问非静态成员变量
}
static int m_A; //静态成员变量
int m_B; //
private:
//静态成员函数也是有访问权限的
static void func2()
{
cout << "func2调用" << endl;
}
};
int Person::m_A = 10;
void test01()
{
//静态成员变量两种访问方式
//1、通过对象
Person p1;
p1.func();
//2、通过类名
Person::func();
//Person::func2(); //私有权限访问不到
}
int main() {
test01();
system("pause");
return 0;
}
18 .const修饰成员函数
常函数:
成员函数后加const后我们称为这个函数为常函数
常函数内不可以修改成员属性
常对象:
声明对象前加const称该对象为常对象
常对象只能调用常函数
19 .继承
- 公共继承:基类的公和保护保持原有访问属性,其私有成员仍为基类私有。
- 私有继承:基类的公和保护变成私有。其私有成员仍为基类私有
- 保护继承:基类的公和保护变成了保护,其私有成员仍为基类私有。
基类和派生类
- 构造函数的主要作用是对数据成员初始化
- 派生类是不能继承基类的析构函数,也需要通过派生类的析构函数去调用基类的析构函数
- 继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
20 .多态
- C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
- 多态 满足条件
有继承关系
子类重写父类中的虚函数 - 多态分为两类
静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
动态多态: 派生类和虚函数实现运行时多态
21 .虚函数
百度百科
求同存异 用指向基类的指针或引用操作对象
-
作用:
允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数,实现多态性,多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。 -
使用方法:
1.在基类用 virtual 声明成员函数为虚函数。
这样就可以在派生类中重新定义此函数。
2.在类外定义虚函数时,不必再加virtual
3.在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数 和 类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。 -
C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此在派生类重新声明该虚函数时,可以加virtual,也可以不加,但习惯上一般在每一层声明该函数时都加virtual,使程序更加清晰
-
纯虚函数
纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是virtual 函数类型 函数名 (参数表列) =0 ; 纯虚函数没有函数体,最后面的 =0 ,并不表示函数返回值为0,它只告诉编译系统“我是纯虚函数”,纯虚函数只有函数的名字而不具备函数的功能,不能被调用。 -
抽象类
凡是包含纯虚函数的类都是抽象类
一个基类如果包含一个或一个以上纯虚函数,就是抽象基类。抽象基类不能也不必要定义对象.
22 数据抽象与数据封装
-
数据抽象只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。抽象把代码分离为接口和实现
-
封装是面向对象编程中的把数据和操作数据的函数 绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。数据封装引申出了另一个重要的 OOP 概念,即数据隐藏。
数据封装是一种把数据和操作数据的函数捆绑在一起的机制,
数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。C++ 通过创建类来支持封装和数据隐藏(public、protected、private)。 -
理想的做法是尽可能地对外隐藏每个类的实现细节。
-
通常情况下,我们都会设置类成员状态为私有(private),除非我们真的需要将其暴露,这样才能保证良好的封装性。
-
数据抽象是一种依赖于接口和实现分离的编程(设计)技术。
23 . C++接口(抽象类)
-
接口:
如果一个抽象类的所有方法都是抽象方法,则可以把这个类用另外一种形式来定义,即接口。
接口特点:
① 一个接口可以被多个类实现;
②接口不能实例化,不能创建构造方法;
③一个接口可以实现多个接口,一个接口可以继承接口;
jdk8.0 之后,接口不仅可以修饰,全局常量,抽象方法,静态方法,默认方法;
接口中的静态方法,只能接口进行调用;
接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。 -
抽象类:
使用 abstract 修饰的类称为抽象类。
抽象类特点:
①抽象类中可以没有抽象方法;
② 类中可以定义抽象方法(abstract修饰的方法);
③ 抽象类不能直接实例化,要通过其普通子类进行实例化,抽象类可以创建构造方法,为子类调用;
④ 子类继承抽象类必须实现抽象类中所有的抽象方法,否则子类也必须定义为抽象类;
⑤ 抽象类修饰符必须为public或者友好的(默认的),不能是private,因为创建抽象类,就是要被其他类继承,用private修饰了,则不能被子类继承,子类便无法实现该方法。 -
接口与抽象类异同点:
①一个类实现接口的话要实现接口的所以方法,而抽象类不一定;
②类可以实现很多个接口,但是只能继承一个抽象类;
③接口中声明的变量默认都是final的。抽象类可以包含非final的变量;
④接口和抽象类都不可以被实例化;
⑤接口不能创建构造方法,抽象类可以创建构造方法;
⑥接口中的静态方法,只能接口进行调用,而抽象类不是;
⑦实现接口的类必须实现其中的所有方法,继承自抽象类的子类实现所有的抽象方法。
24 .文本文件
-
#include < fstream >
-
文件类型分为两种:
文本文件 - 文件以文本的ASCII码形式存储在计算机中;
二进制文件 - 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们。 -
操作文件的三大类:
ofstream:写操作
ifstream: 读操作
fstream : 读写操作
//*****写 文 本 文 件********
ofstream ofs;//创建流对象
ofs.open("test.txt", ios::out);//打开文件.open(“文件路径”,打开方式);
//文件打开方式可以配合使用,利用|操作符
//**例如:**用二进制方式写文件 ios::binary | ios:: out
ofs << "姓名:张三" << endl;//写数据ofs << “写入的数据”;
ofs.close();//关闭文件
/*
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在先删除,再创建
ios::binary 二进制方式
*/
//*****读 文 本 文 件********
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//第一种方式
//char buf[1024] = { 0 };
//while (ifs >> buf)
//{
// cout << buf << endl;
//}
//第二种
//char buf[1024] = { 0 };
//while (ifs.getline(buf,sizeof(buf)))
//{
// cout << buf << endl;
//}
//第三种
//string buf;
//while (getline(ifs, buf))
//{
// cout << buf << endl;
//}
char c;
while ((c = ifs.get()) != EOF)
{
cout << c;
}
ifs.close();
}
- 以二进制的方式对文件进行读写操作打开方式要指定为 ios::binary,
写文件利用流对象调用成员函数ostream& write(const char * buffer,int len);,字符指针buffer指向内存中一段存储空间。len是读写的字节数。
//二进制文件 写文件
void test01()
{
//1、包含头文件
//2、创建输出流对象
ofstream ofs("person.txt", ios::out | ios::binary);
//3、打开文件
//ofs.open("person.txt", ios::out | ios::binary);
Person p = {
"张三" , 18};
//4、写文件
ofs.write((const char *)&p, sizeof(p));
//5、关闭文件
ofs.close();
}
- 二进制方式读文件主要利用流对象调用成员函数:**istream& read(char *buffer,int len);**字符指针buffer指向内存中一段存储空间。len是读写的字节数。
#include <fstream>
#include <string>
class Person
{
public:
char m_Name[64];
int m_Age;
};
void test01()
{
ifstream ifs("person.txt", ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
}
Person p;
ifs.read((char *)&p, sizeof(p));
cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
char data[100];
// 以写模式打开文件
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// getline()函数从外部读取一行
//ignore() 函数会忽略掉之前读语句留下的多余字符。
// 向文件写入用户输入的数据
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// 再次向文件写入用户输入的数据
outfile << data << endl;
// 关闭打开的文件
outfile.close();
// 以读模式打开文件
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// 在屏幕上写入数据
cout << data << endl;
// 再次从文件读取数据,并显示它
infile >> data;
cout << data << endl;
// 关闭打开的文件
infile.close();
return 0;
}
- 文件位置指针
istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。这些成员函数包括关于 istream 的 seekg(“seek get”)和关于 ostream 的 seekp(“seek put”)。
seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。查找方向可以是 ios::beg(默认的,从流的开头开始定位),也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。
文件位置指针是一个整数值,指定了从文件的起始位置到指针所在位置的字节数。下面是关于定位 “get” 文件位置指针的实例:
// 定位到 fileObject 的第 n 个字节(假设是 ios::beg)
fileObject.seekg( n );
// 把文件的读指针从 fileObject 当前位置向后移 n 个字节
fileObject.seekg( n, ios::cur );
// 把文件的读指针从 fileObject 末尾往回移 n 个字节
fileObject.seekg( n, ios::end );
// 定位到 fileObject 的末尾
fileObject.seekg( 0, ios::end );
25 .输入输出流
-
标准输出流:cout 、cerr、clog
cerr、clog流对象都是标准错误流,都是在终端显示器上显示出错信息 -
标准输入流:cin、get、getline
get函数读入一个字符,getline读入一行字符
eof函数:文件结束
peek函数:peek函数的作用是观测下一个字符。 -
putback函数:其作用是将前面用get或getline函数从输入流中读取的字符ch返回到输入流,插入到当前指针位置,以供后面读取
-
ignore函数:函数作用是跳过输入流中n个字符,或在遇到指定的终止字符时提前结束
26 C++ 异常处理
- 异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw.
throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
#include <iostream>
using namespace std;
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}
int main ()
{
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}
27 . 预处理器
-
#define 预处理
#define 预处理指令用于创建符号常量。该符号常量通常称为宏,
#define PI 3.14159
#define MIN(a,b) (a<b ? a : b) -
条件编译
有几个指令可以用来有选择地对部分程序源代码进行编译。这个过程被称为条件编译。
条件预处理器的结构与 if 选择结构很像。请看下面这段预处理器的代码:
#ifdef NULL
#define NULL 0
#endif -
. # 和 ## 运算符
-
预定义宏
28 . typedef 声明
可以使用 typedef 为一个已有的类型取一个新的名字。下面是使用 typedef 定义一个新类型的语法:
typedef type newname;
typedef int feet;//feet 是 int 的另一个名称:
29 .枚举类型
- 枚举类型(enumeration)是C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。
创建枚举,需要使用关键字 enum。枚举类型的一般形式为:
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
...
标识符[=整型常数]
} 枚举变量;
- 如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始。
例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 “blue”。
enum color { red, green, blue } c;
c = blue;
默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green 的值为 5。 - enum color { red, green=5, blue };
在这里,blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1,但 red 的值依然为 0。