Cherno CPP学习笔记-03-高级特性

2024-04-08 12:36

本文主要是介绍Cherno CPP学习笔记-03-高级特性,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.12、再打一天渔吧

P27、C++继承

讲了一些思想,还是挺好的,就不记了。

P28、C++虚函数

虚函数引入了一种叫做Dynamic Dispatch (动态联编)的机制,通常通过虚函数表(v表)来实现编译。

  • 虚函数表就是一个表,包含所有基类中所有虚函数的映射,这样我们在运行时可以将他们映射到正确的覆写(override)函数

  • 虚函数有额外开销。一方面,需要额外的内存来存储v表,这样我们就可以分配到正确的函数,包括基类中要有一个成员指针,指向v表;另一方面,每次调用虚函数时,我们需要遍历这个表,来确定要映射到哪个函数。

为什么需要虚函数的一个例子:

#include<iostream>
#include<string>
//这里不使用虚函数
class Entity 
{
public:std::string GetName() { return "Entity"; }
};class Player : public Entity
{
private:std::string m_name;
public:Player(const std::string& name): m_name(name) {}std::string GetName() { return m_name; }
};
int main() {Entity* e = new Entity();std::cout << e->GetName() << std::endl;Player* p = new Player("Cherno");std::cout << p->GetName() << std::endl;//输出://Entity//ChernoEntity* entity = p;std::cout << entity->GetName() << std::endl;//输出://Entity  //无法实现多态原因:我们在声明函数时,我们的方法通常在类内部起作用。//当要调用方法的时候,会调用属于该类型的方法std::cin.get();
}

用虚函数改进仅需要将Entity类的方法返回值前加上virtual关键字

class Entity 
{
public:virtual std::string GetName() { return "Entity"; }
};

同时最好将Player类中对应的方法加上override (C++11特性),防止拼写错误

std::string GetName() override { return m_name; }

P29、C++接口(纯虚函数)

纯虚函数允许我们在基类中定义一个没有实现的函数,然后强制子类去实现该函数。

实现语法:

class Entity 
{
public:virtual std::string GetName() = 0;
};

一个例子:(GetClassName接口)

#include<iostream>
#include<string>class Printable
{
public:virtual std::string GetClassName() = 0;
};class Entity : public Printable
{
public:std::string GetClassName() override { return "Entity"; }
};class Player : public Entity
{
public:std::string GetClassName() override { return "Player"; }
};void Print(Printable* obj) 
{std::cout << obj->GetClassName() << std::endl;
}int main() {Entity* e = new Entity();Player* p = new Player();Entity* entity = p;Print(e);Print(p);Print(entity);//输出://Entity//Player//Playerstd::cin.get();
}

P30、C++可见性

可见性是属于面向对象编程的概念,它指的是类的某些成员或方法实际上有多可见:谁能看见他们、谁能调用他们、谁能使用它们;

C++中有三个基础的可见性修饰符:(Java中还有default、C#中有internal)

  • private:只有这个类本身能访问所修饰的符号,友元除外。

    • friend是C++关键字,它可以让类或者函数成为类的朋友(友元),可以从类中访问私有成员。
  • protect:只有这个类本身和层次结构中的所有子类可以访问所修饰的符号,友元除外?

  • public:公开访问。

P31、C++数组

C++11库中有标准数组std::array,优点有边界检查、记录数组大小等。

堆区new出来的数组,对指针使用sizeof()没有意义。所以很多情况下需要我们自己维护数组的大小。

如果在类中申请栈上的数组:

static const int exampleSize = 5;
int example[exampleSize];

使用std::array:

#include <array>std::array<int, 5> another;
int size = another.size();

P32、字符串

没什么东西,不如学STL。

1.13、三天打渔

P33、C++字符串字面量

字符串字面量,是在双引号之间的一串字符。

字符串字面量永远保存在内存中的只读区域内

char name[] = "Cherno";
//在汇编模式中,‘Cherno’被保存到只读区,name指针保存的是一个只读区的地址。
name[2] = 'a';
//如果要修改,寄存器会复制只读区的'Cherno'到name变量中,产生了额外开销。(不能直接修改只读区的内存)

VS的编译器是MSVC。

//一个字节的字符 utf8
const char* name = u8"Cherno";//宽字符wchar_t windows上是2字节,linux上是4字节,mac也是4字节?
const wchar_t* name1 = L"Cherno";//两个字节的字符 utf16
const char16_t* name2 = u"Cherno";//四个字节的字符 utf32
const char32_t* name3 = U"Cherno";
//原版
std::string name0 = (std::string)"hello" + ", Alice";//C++14新特性
using namespace std::string_literals;
std::string name1 = "hello"s + ", Alice";std::wstring name2 = L"hello"s + L", Alice";std::u16string name3 = u"hello"s + u", Alice";std::u32string name4 = U"hello"s + U", Alice";//原版const char* example = "line1\n""line2\n""line3\n";//C++14新特性  R()忽略转义字符const char* ex = R"(line1line2line3)";

P34、C++中的CONST

const int* a = new int;
//星号前,不能修改指针指向的内容 如: *a = 2;int* const a = new int;
//星号后,不能修改指针的指向 如 a = &b;
class Entity
{
private:int m_x, m_y;mutable int var;
public://类中方法括号后的const意为这个方法不会修改任何实际的类,只能读,不能写;mutable修饰的变量除外int GetX() const{//m_x = 2; 不被允许var = 2; //可以return m_x;}int GetX(){return m_x;}};//参数const约定不修改e的内容, &防止拷贝,优化性能
void PrintEntity(const Entity& e)
{//此处必须调用带const修饰的GetX函数std::cout << e.GetX() << std::endl;
}

P35、C++的mutable关键字

mutable标记一个变量,意味着类中的const方法可以修改这个成员。

mutable的用途:

  • 与const一起使用,如P34中,想要统计GetX()的调用次数,最好设置一个成员 mutable int cnt;
  • 在lambda表达式中使用
//使用mutable修饰lambda表达式允许传值的条件下修改内部x的值,但此时外部x的值没有改变
int x = 8;
auto f = [=]() mutable
{x++;std::cout << x << std::endl;
}f();
//x == 8; 

P36、C++的成员初始化列表

语法:

class Entity
{
private:std::string m_Name;int m_x, m_y;
public://初始化列表中最好按照声明的顺序写,防止警告Entity():m_Name("Unkonwn"),m_x(0),m_y(5){}Entity(const std::string& name):m_Name(name){}};
  • 便于维护易读性:函数体内部可能会有Init之类的其他操作;

  • 提升性能:

#include<iostream>
#include<string>
class Example
{
public:Example(){std::cout << "Create Example !" << std::endl;}Example(int x){std::cout << "Create Example with" << x << std::endl;}
};class Entity
{
private:std::string m_Name;Example m_Example;
public://初始化列表中最好按照声明的顺序写,防止警告Entity(){m_Name = "Unknown";m_Example = Example(8);}Entity(const std::string& name):m_Name(name), m_Example(Example(8)){}};int main() {Entity e1;//输出://Create Example !//Create Example with8Entity e2("Cherno");//输出://Create Example with8std::cin.get();
}
//不使用初始化列表的情况,在Entity构造函数中相当于:
Entity(){Example m_Example;m_Name = "Unknown";m_Example = Example(8);}
//会有两次Example的构造过程//而使用初始化列表相当于声明的时候就赋初值:
Example m_Example = Example(8);
//故只调用一次

P37、C++的三元操作符

?: 没什么东西

P38、 创建并初始化C++对象

栈上创建还是堆上创建。

  • 如果对象很大,或者想要显式地控制对象地生存期,在堆上创建。

    • 需要手动delete,忘了就内存泄漏
    • 智能指针可以自动delete。(在没有引用时)
  • 反之在栈上创建。(栈大小一般只有几M)

P39、C++ new关键字

//malloc做的仅仅是分配内存,然后给我们一个指向那个内存的指针(C++中不推荐使用)
Entity* e = (Entity*)malloc(sizeof(Entity)) ;
//new相比malloc多做的是调用了构造函数
Entity* e = new Entity();

P40、C++隐式转换与explicit关键字

隐式的意思是,不会明确地告诉他要做什么,有点像自动,通过上下文知道意思。

C++允许编译器对代码进行一次隐式转换。

如果我们开始有一个数据类型,然后有另一种类型,在两者之间,C++允许隐式进行转换,而不需要用cast做强制转换。

class Entity
{
private:std::string m_Name;int m_Age;
public:Entity(int age):m_Name("Cherno"),m_Age(age) {}Entity(const std::string& name):m_Name(name),m_Age(-1) {}};void PrintEntity(const Entity& entity)
{//Printing
}int main() {Entity e1(22);Entity e2("Cherno");//隐式转换 或者称为 隐式构造函数Entity e3 = 22;Entity e4 = (std::string)"cherno";//另一种隐式转换PrintEntity(22);std::cin.get();
}

explicit关键字:禁用隐式转换的功能

  • explicit关键字放在构造函数前面,表示不能隐式转换,必须显式调用此构造函数
  • 例子略

P41、C++运算符及其重载

运算符:

  • 加减乘除、逆向引用、箭头、+=、&、<<、new、delete、逗号、括号等
class Vector2
{
public:float x, y;Vector2(float x, float y) : x(x), y(y) {}Vector2 operator+(const Vector2& other) const{return Vector2(x + other.x, y + other.y);}Vector2 operator*(const Vector2& other) const{return Vector2(x * other.x, y * other.y);}bool operator==(const Vector2& other){return x == other.x && y == other.y;}
};std::ostream& operator<<(std::ostream& stream, Vector2& vector)
{stream << vector.x << " ," << vector.y << std::endl;return stream;
}
int main() {Vector2 position(4.0f, 4.0f);Vector2 speed(0.5f, 1.5f);Vector2 powerUp(1.1f,1.1f);Vector2 result = position + speed * powerUp;std::cout << result << std::endl;std::cin.get();
}

P42、C++的this关键字

没什么东西。

P43、C++的对象生存期(栈、作用域、生存期)

作用域Scope:

  • if作用域、for、while、空作用域、类作用域

智能栈指针?一个例子:(离开作用域后自动被销毁)其他用途:计时器timer、互斥锁mutex locking 等。

class Entity
{
public:Entity() { std::cout << "Created Entity!" << std::endl; }~Entity() { std::cout << "Delete Entity!" << std::endl; }
};class ScopePtr
{
private:Entity* m_Ptr;
public:ScopePtr(Entity* entity) :m_Ptr(entity) {}~ScopePtr(){delete m_Ptr;}
};int main() {{//ScopePtr对象e是在栈上分配的,e如果被删除,析构函数删除Entity对象ScopePtr e = new Entity();}std::cin.get();
}

P44、C++的智能指针

智能指针是自动实现new和delete操作的一种方式。当你new之后不需要delete,甚至不用new。

本质上是一个原始指针的一个包装。

第一个智能指针:unique_ptr:作用域指针

//unique_ptr不能被copy,防止二次释放
class Entity
{
public:Entity() { std::cout << "Created Entity!" << std::endl; }~Entity() { std::cout << "Delete Entity!" << std::endl; }void Print() { std::cout << "Print" << std::endl; }
};class ScopePtr
{
private:Entity* m_Ptr;
public:ScopePtr(Entity* entity) :m_Ptr(entity) {}~ScopePtr(){delete m_Ptr;}
};int main() {{//智能作用域指针unique_ptrstd::unique_ptr<Entity>entity(new Entity());//std::unique_ptr<Entity>entity = new Entity();  不能这样因为explicit修饰/*template <class _Dx2 = _Dx, _Unique_ptr_enable_default_t<_Dx2> = 0>_CONSTEXPR23 explicit unique_ptr(pointer _Ptr) noexcept : _Mypair(_Zero_then_variadic_args_t{}, _Ptr) {}*///更推荐的做法:(如果构造函数抛出异常,这种方法更安全)std::unique_ptr<Entity>entity2 = std::make_unique<Entity>();//entity2 = entity; 不能copy,因为unique_ptr的拷贝构造函数被删除了entity->Print();}std::cin.get();
}

引用计数指针:shared_ptr:引用计数可以跟踪指针有多少个引用,一旦引用计数达到0,他就被删除了。

std::shared_ptr<Entity> entity = std::make_shared<Entity>(); 	//更推荐使用
std::shared_ptr<Entity> entity(new Entity());
  • shared_ptr需要额外分配一块内存,叫做控制块,用来存储引用计数。
  • 如果首先创建一个new Entity,然后将其传递给shared_ptr构造函数,他必须做两次内存分配,
    • 先做一次new Entity,然后是shared_ptr的控制内存块的分配。
    • 如果把他们组合起来会更有效率。
	{std::shared_ptr<Entity> e0;{std::shared_ptr<Entity> entity = std::make_shared<Entity>();e0 = entity;}//此处entity不释放,因为还有e0在引用它}//e0回收,entity将在此处被释放

弱指针:weak_ptr:与shared_ptr一起使用,但不会增加shared_ptr的引用计数

	{std::weak_ptr<Entity> w0;{std::shared_ptr<Entity> entity = std::make_shared<Entity>();w0 = entity;//不会增加引用计数}//此处entity释放//可以随时使用weak_ptr的方法查看是否过期}

这篇关于Cherno CPP学习笔记-03-高级特性的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/885603

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个