总结在算法中常见的C++知识点,用于学习记录
目录
1、封装
- 封装的意义:
(1)将属性和行为作为一个整体,表现生活中的事物
(2)将属性和行为加以权限控制(public公共权限、struct保护权限、private私有权限)
语法:class 类名{ 访问权限: 属性 / 行为 };
属性:成员属性/成员变量
行为:成员函数/成员方法
//学生类
class Student {
public:
void setName(string name) {
m_name = name;
}
void setID(int id) {
m_id = id;
}
void showStudent() {
cout << "name:" << m_name << " ID:" << m_id << endl;
}
public:
string m_name;
int m_id;
};
int main() {
Student stu;
stu.setName("德玛西亚");
stu.setID(250);
stu.showStudent();
system("pause");
return 0;
}
2、继承
- 作用:减少重复代码
class A :public B;
语法:class 子类 : 继承方式 父类
3、多态
静态多态(早绑定): 函数重载 和 运算符重载属于静态多态,复用函数名
动态多态(晚绑定): 派生类和虚函数实现运行时多态
多态满足条件:
(1)有封装和继承关系
(2)子类重写父类中的虚函数
多态使用条件:父类指针或引用指向子类对象
class Animal
{
public:
//Speak函数就是虚函数
//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
class Cat :public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
class Dog :public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
//我们希望传入什么对象,那么就调用什么对象的函数
//如果函数地址在编译阶段就能确定,那么静态联编
//如果函数地址在运行阶段才能确定,就是动态联编
void DoSpeak(Animal & animal)
{
animal.speak();
}
//
//多态满足条件:
//1、有继承关系
//2、子类重写父类中的虚函数
//多态使用:
//父类指针或引用指向子类对象
void test01()
{
Cat cat;
DoSpeak(cat);
Dog dog;
DoSpeak(dog);
}
int main() {
test01();
system("pause");
return 0;
}
4、函数重载
- 作用:函数名相同,提高复用性
满足条件:
(1)同一个作用域下
(2)函数名称相同
(3)函数参数类型不同、个数不同、顺序不同
5、运算符重载
(1)加号运算符重载
(2)左移运算符重载
(3)递增运算符重载
(4)赋值运算符重载
(5)关系运算符重载
6、引用
- 作用:给变量起别名
语法:数据类型 &别名 = 原名
int &b = a;
三种传递
(1)值传递:形参不会修饰实参
(2)地址传递:形参会修饰实参
(3)引用传递:形参会修饰实参
//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;
}
int main() {
int a = 10;
int b = 20;
mySwap01(a, b);
cout << "a:" << a << " b:" << b << endl;
mySwap02(&a, &b);
cout << "a:" << a << " b:" << b << endl;
mySwap03(a, b);
cout << "a:" << a << " b:" << b << endl;
system("pause");
return 0;
}
- 引用的本质:在C++内部实现一个指针常量
//发现是引用,转换为 int* const ref = &a;指针的指向不可以改,指针指向的值可以改
void func(int& ref){
ref = 100; // ref是引用,转换为*ref = 100
}
7、内存四区
(1)代码区:存放函数体的二进制代码,操作管理系统
(2)全局区:存放全局变量、静态变量和常量(除const修饰的局部变量)
(3)栈区:由编译器自动分配释放,存放函数的参数值、局部变量等
(4)堆区:由程序员分配释放,若程序不释放,程序结束时由操作系统回收,在C++主要利用new在堆区开辟内存
8、new操作符
new 是一个运算符,用于在动态内存中分配空间以创建对象,并返回指向该对象的指针。它通常用于创建动态对象,这些对象的生存期不受限制,并且可以在程序的任何位置进行释放。
用法:
T *ptr = new T;
- T 是要创建的对象类型。
- ptr 是一个指针,指向新对象的地址
示例:使用 new 来动态分配一个整数和一个自定义类的对象
// 动态分配一个整数
int *intPtr = new int;
*intPtr = 42;
// 动态分配一个自定义类的对象
class MyClass {
public:
MyClass(int value) : data(value) {}
void print() { std::cout << "Value: " << data << std::endl; }
private:
int data;
};
MyClass *objPtr = new MyClass(10);
objPtr->print();
// 记得在不再需要时释放动态分配的内存
delete intPtr;
delete objPtr;
在动态分配内存后,你需要负责手动释放这些内存以避免内存泄漏。释放动态分配的内存使用 delete 运算符
9、构造函数和析构函数
(1)构造函数:进行初始化操作
(2)析构函数:进行清理的操作
构造函数语法:类名(){}
析构函数语法: ~类名(){}
拷贝构造函数:
class Person {
public:
//拷贝构造函数
Person(const Person& p) {
age = p.age;
cout << "拷贝构造函数!" << endl;
}
};
10、深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
总结:一个构造函数里面可以有浅拷贝和深拷贝,堆区拷贝是深拷贝,简单赋值是浅拷贝
class Person {
public:
//无参(默认)构造函数
Person() {
cout << "无参构造函数!" << endl;
}
//有参构造函数
Person(int age ,int height) {
cout << "有参构造函数!" << endl;
m_age = age;
m_height = new int(height);
}
//拷贝构造函数
Person(const Person& p) {
cout << "拷贝构造函数!" << endl;
//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
m_age = p.m_age;
m_height = new int(*p.m_height);
}
//析构函数
~Person() {
cout << "析构函数!" << endl;
if (m_height != NULL)
{
delete m_height;
}
}
public:
int m_age;
int* m_height;
};
void test01()
{
Person p1(18, 180);
Person p2(p1);
cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;
cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
11、static
(1)修饰普通变量:修改变量的存储区域和生命周期,使变量存储在静态区。
(2)修饰普通函数:表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为
了防止与他人命令函数重名,可以将函数定位为 static。
(3)修饰静态成员变量: 所有对象共享同一份数据、在编译阶段分配内存、类内声明,类外初始化
(4)修饰静态成员函数:所有对象共享同一个函数、静态成员函数只能访问静态成员变量
静态成员变量
class Person
{
public:
static int m_A; //静态成员变量
private:
static int m_B; //静态成员变量也是有访问权限的
};
静态成员函数
class Person
{
public:
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(); //私有权限访问不到
}
12、Const
- 作用:
(1)修饰变量,说明该变量不可以被改变
(2)修饰指针,包括常量指针、指针常量、既修饰指针,又修饰常量
(3)常量引用,用来修饰形参,防止误操作
(4)修饰成员函数,成员函数加const后为常函数,不可以修改成员变量
修饰指针
//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; //错误
常量引用
//引用使用的场景,通常用来修饰形参
void showValue(const int& v) {
//v += 10;
cout << v << endl;
}
int main() {
//int& ref = 10; 引用本身需要一个合法的内存空间,因此这行错误
//加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;
const int& ref = 10;
//ref = 100; //加入const后不可以修改变量
cout << ref << endl;
//函数中利用常量引用防止误操作修改实参
int a = 10;
showValue(a);
system("pause");
return 0;
}
修饰成员函数
class Person {
public:
Person() {
m_A = 0;
m_B = 0;
}
//this指针的本质是一个指针常量,指针的指向不可修改
//如果想让指针指向的值也不可以修改,需要声明常函数
void ShowPerson() const {
//const Type* const pointer;
//this = NULL; //不能修改指针的指向 Person* const this;
//this->mA = 100; //但是this指针指向的对象的数据是可以修改的
//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
this->m_B = 100;
}
void MyFunc() const {
//mA = 10000;
}
public:
int m_A;
mutable int m_B; //可修改 可变的
};
//const修饰对象 常对象
void test01() {
const Person person; //常量对象
cout << person.m_A << endl;
//person.mA = 100; //常对象不能修改成员变量的值,但是可以访问
person.m_B = 100; //但是常对象可以修改mutable修饰成员变量
//常对象访问成员函数
person.MyFunc(); //常对象不能调用const的函数
}
int main() {
test01();
system("pause");
return 0;
}
13、this指针
this 指针是一个隐含于每一个非静态成员函数中的特殊指针。this指针指向被调用的成员函数所属的对象
- this指针的用途:
(1)当形参和成员变量同名时,可用this指针来区分
(2) 在类的非静态成员函数中返回对象本身,可使用return *this
class Person
{
public:
Person(int age)
{
//1、当形参和成员变量同名时,可用this指针来区分
this->age = age;
}
Person& PersonAddPerson(Person p)
{
this->age += p.age;
//返回对象本身
return *this;
}
int age;
};
void test01()
{
Person p1(10);
cout << "p1.age = " << p1.age << endl;
Person p2(10);
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
cout << "p2.age = " << p2.age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
14、friend(友元)
- 作用:友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字为 friend
友元的三种实现:
(1) 全局函数做友元
(2) 类做友元
(3) 成员函数做友元
类做友元
class Building;
class goodGay
{
public:
goodGay();
void visit();
private:
Building *building;
};
class Building
{
//告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容
friend class goodGay;
public:
Building();
public:
string m_SittingRoom; //客厅
private:
string m_BedRoom;//卧室
};
Building::Building()
{
this->m_SittingRoom = "客厅";
this->m_BedRoom = "卧室";
}
goodGay::goodGay()
{
building = new Building;
}
void goodGay::visit()
{
cout << "好基友正在访问" << building->m_SittingRoom << endl;
cout << "好基友正在访问" << building->m_BedRoom << endl;
}
void test01()
{
goodGay gg;
gg.visit();
}
int main(){
test01();
system("pause");
return 0;
}
15、namespace( 命名空间)
在C++中,namespace 是一种用于组织和管理代码的机制。它允许你将相关的变量、函数、类等封装在一个命名空间中,以避免命名冲突并提高代码的可维护性。
- 以下是相关使用
(1)命名空间定义
使用namespace 关键字定义一个命名空间,将相关的代码放入其中。命名空间通常位于全局作用域内,但也可以嵌套在其他命名空间内。
namespace MyNamespace {
// 变量、函数、类等代码放在这里
}
(2)使用命名空间
命名空间被定义,可以使用 :: 操作符来引用命名空间中的元素。例如,如果有一个函数 myFunction 在 MyNamespace 中,你可以这样调用它:
MyNamespace::myFunction();
(3)避免命名冲突
命名空间的一个主要作用是避免全局命名冲突。如果你有两个不同的库或代码文件,它们都有一个名为 myFunction 的函数,你可以将它们放入不同的命名空间,从而避免冲突。
(4)嵌套命名空间
namespace OuterNamespace {
namespace InnerNamespace {
// 变量、函数、类等代码放在这里
}
}
(5)使用命名空间别名
为一个命名空间定义一个别名,以简化代码。这在较长或复杂的命名空间名称上特别有用。
namespace MyNS = MyNamespace;
(6)匿名命名空间
C++还提供了匿名命名空间,这是一个没有名字的命名空间,其内容在当前编译单元内是唯一的。它常用于定义仅在当前文件内可见的全局变量和函数
namespace {
int x = 42; // 仅在当前文件内可见
}
16、enum(枚举)
枚举允许你为一组相关的整数常量定义具有有意义的名称,以提高代码的可读性和可维护性。
- 用于以下情况:
(1)定义枚举类型:
enum Color {
Red,
Green,
Blue
};
(2)显式赋值:为枚举常量分配整数值
enum Status {
OK = 0,
Error = 1,
Unknown = 999
};
(3)使用枚举类型:声明变量,函数参数或函数返回值为枚举类型,以表示一组离散的状态或选项
Color chosenColor = Red;
Status systemStatus = OK;
(4)使用枚举值:进行比较和操作
if (chosenColor == Red) {
// 执行红色相关的操作
}
(5)枚举的作用域
枚举类型和它的枚举常量都在定义它们的作用域内可见。通常,它们被定义在全局作用域或类的内部作用域中
(6)使用枚举类(C++11及以后)
C++11 引入了强类型的枚举,也称为枚举类。枚举类提供更严格的类型检查,防止错误的枚举值被使用。定义一个枚举类如下:
enum class Color {
Red,
Green,
Blue
};
使用枚举类时,你需要指定枚举值的类型,例如:
Color chosenColor = Color::Red;
17、static_cast
static_cast
是 C++ 中的一个类型转换操作符,用于执行编译时类型转换。它可以将一个表达式的类型强制转换为另一个类型,前提是这种类型转换是安全的
- 用于以下情况:
(1)基本数据类型之间的转换,例如将整数类型转换为浮点数类型
int integerNumber = 42;
double doubleNumber = static_cast<double>(integerNumber);
(2)子类指针或引用到基类指针或引用的转换:多态的情况下,将子类对象指针或引用转换为基类指针或引用。
class Base { /* ... */ };
class Derived : public Base { /* ... */ };
Derived derivedObj;
Base* basePtr = static_cast<Base*>(&derivedObj);
(3)枚举类型转换:使用 static_cast 来执行不同枚举类型之间的转换。
enum class Color { Red, Green, Blue };
int enumValue = static_cast<int>(Color::Green);
(4)显式类型转换
double pi = 3.14159265359;
int approximatedPi = static_cast<int>(pi);