C++随心记

news2024/11/6 3:17:19

C++随心记

C++中的 CONST

C++中的const是表示不可修改

int main()
{
    /* 对于变量而言 */

    // 不可修改的常量
    const int A = 10;
    // 不可修改的指针指向
    const int* pointer_0 = nullptr;
    int const* poniter_1 = nullptr;
    // 不可修改指针指向的内容
    int* const poniter_2 = nullptr;
}

const也可修饰函数

class Entity
{
private:
    int* pointer;
    int x;
public:
    /* 对于函数而言 */

    // 修饰函数,使得其权限仅为读取,不可写入
    int GetX() const
    {
        // x++; 此语句是非法的,函数被const修饰后不准更改函数外的变量
        return x;
    }
    // 如果返回的为指针也可以用于修饰指针
        // 指向内容不可变
    int* const GetPoniter()
    {
        return pointer;
    }
        // 指向不可变
    const int* GetPointer()
    {
        return pointer;
    }
    // 一次性写最多的合法const
    const int* const GetPointer() const
    {
        return pointer;
    }

};

C++中的mutable

和上文的const一样,都是关键字,但mutable与const相反。mutable意味着可更改的。

class Entity
{
private:
    mutable int x;
    int y;
public:
    Entity()
        : x(10), y(20)
    {}

    void ChangeXAndRead() const
    {
        x++;
        std::cout << "The value of X,Y is " << x << ',' << y << std::endl;
        // 这里的输出结果为11,20
        // 当试图修改y的值的时候,因为y的值为非mutable修饰,所以会报错
    }
};

C++中的成员初始化列表

在面向对象的语言中,多数程序员喜欢在构造器内初始化成员。在C++语法中,存在专门初始化成员的功能,此功能的优点是:避免了在定义成员时被构造,当我们定义一个变量时,如果这个变量为某个类的实例,那么在定义时会调用其构造器,当我们赋值时回再次调用构造器,这造成了性能的浪费。

class Entity
{
public:
    std::string x;  // 此时调用了string的构造器

    Entity()
    {
        x = "Hello";    // 此时调用了"Hello"的构造器,并把地址给了x
    }
};

// 下面是一个比较直观的例子
class Example
{
public:
    Exapmle()
    {
        std::cout << "Example created!" << std::endl; 
    }

    Example(int x)
    {
        std::cout << "Example created with x! x=" << x << std::endl; 
    }
};

class Entity
{
public:
    Example example;
    // 这里会输出Example created!
    Entity()
    {
        example = Example(8);
        // 这里会输出Example created with x! x=8
    }
};

这样就输出了两次,代表着调用了两次构造器。为了节省性能,我们可以使用成员初始化列表,语法如下

class Entity
{
private:
    std::string m_Name;
    int m_DebugCount;
public:
    Entity(const std::string& Name)
        :m_Name(Name), m_DebugCount(0)
    {}
};

上面写了一个有参构造器,参变量为常量引用的Name。重点是下面是成员初始化列表,用冒号隔开,冒号后为要初始化的成员,初始化的内容为后面的括号的内容。需要注意的是,成员初始化列表需要和成员变量的定义顺序相同,这一规则非严格规定,但不遵守此规则可能会导致部分成员变量无法初始化!每个成员变量之间用逗号隔开

delete的小注意

对于new出来的对象,要记得delete。要delete一块数组,要用delete[]

int main()
{
    int* a = new int[10];
    delete[] a; // To delete array a
    return 0;
}

如果不使用delete[]的话,你删的只是指针所指的第一个元素而已。

C++的隐式类型转换

C++中的类型转换是自动的,甚至可以转换到类的构造器中,以下为实例。

class Entity
{
private:
    int m_Age;
    std::string m_Name;
public:
    Entity(const std::string& name)
        :m_Age(-1), m_Name(name)
    {
        std::cout << "Entity constructor with name has called!" << std::endl;
    }

    Entity(int age)
        :m_Age(age), m_Name("Unknown")
    {
        std::cout << "Entity constructor with age has called!" << std::endl;
    }
};

int main()
{
    Entity e1 = 12; // 隐式类型转换
    Entity e2 = std::string("Chreno");
    return 0;
}

explicit关键字

由于隐式转换是默认的,开发者不希望自己的构造函数被隐式转换就可以添加关键字explicit来强制构造器拒绝隐式转换。例如

class Entity
{
private:
    int m_Age;
    std::string m_Name;
public:
    Entity(const std::string& name)
        :m_Age(-1), m_Name(name)
    {
        std::cout << "Entity constructor with name has called!" << std::endl;
    }

    explicit Entity(int age)
        :m_Age(age), m_Name("Unknown")
    {
        std::cout << "Entity constructor with age has called!" << std::endl;
    }
};

int main()
{
    // Entity e1 = 12; // 此行由于构造器被explicit修饰故报错
    Entity e2 = std::string("Chreno");
    return 0;
}

C++中的运算符重载

浅谈一下重载问题,其实运算符最简单的四则运算无非就是加减乘除。我们在重载时只要跟编译器说明了这个函数就是在重载就好了,我们会用到operator标识一下我们重载的是运算符

struct Vector2
{
    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);
    }
    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);
    }

    Vector2 Add(const Vector2& other) const
    {
        return Vector2(x + other.x, y + other.y);
    }
    Vector2 Multiply(const Vector2& other) const
    {
        return Vector2(x * other.x, y * other.y);
    }

    void printSelf()
    {
        std::cout << "Value of x:" << x << ",Value of y:" << y << std::endl;
    }
};


int main()
{
    Vector2 position(4.0f, 4.0f);
    Vector2 speed(0.5f, 1.5f);
    Vector2 powerup(1.1f, 1.1f);

    Vector2 result = position.Add(speed.Multiply(powerup));
    Vector2 res = position + speed * powerup;

    result.printSelf();
    res.printSelf();
    // result equals res
    return 0;
}

再补充一点,我们可以重载ostream的<<运算符来达到输出内容的目的

#include <iostream>

class Entity
{
public:
    int x, y;

    Entity(int x, int y)
        :x(x), y(y)
    {}

};

// 2、此时我们就需要重载一下运算符
std::ostream& operator<<(std::ostream& stream, const Entity& other)
{
    stream << other.x << ',' << other.y << std::endl;
    return stream;
}

int main()
{
    Entity e(1, 2);
    // std::cout << e << std::endl; // 1、这里肯定不会输出其内容,而且也会报错

    // 3、然后我们再来使用这个运算符
    std::cout << e << std::endl;
    return 0;
}

C++智能指针

智能指针就是用std::unique_ptr<>来裹住一个对象,相对于原始指针,智能指针加了一层壳。将这个对象的栈和堆内存绑定,在跳出作用域时自动调用delete方法,语法见下

#include <iostream>
#include <memory>

class Entity
{
public:
    int x, y;

    Entity(int x, int y)
        :x(x), y(y)
    {
        std::cout << "Entity created!" << std::endl;
    }
    ~Entity()
    {
        std::cout << "Entity destory by itself" << std::endl;
    }

};

int main()
{
    {
        std::unique_ptr<Entity> entity = std::make_unique<Entity>();    // 就一次内存分配,更效率
        std::unique_ptr<Entity> e(new Entity(1, 2));    // 先Entity()构造分配一次内存,再给unique_ptr分内存
    }
    
    std::cout << "Program is going done~" << std::endl;
    return 0;
}

但是unique_ptr不能共享指针给别人,从原理上来说,这个对象被释放以后手握这个对象的指针也都会嗝屁,实际上需要同步。从源码上来看,=号这个运算符被重载了。
下面看一下shared_ptr,一个可以共享的智能指针

#include <iostream>
#include <memory>

class Entity
{
public:
    int x, y;

    Entity(int x, int y)
        :x(x), y(y)
    {
        std::cout << "Entity created!" << std::endl;
    }
    ~Entity()
    {
        std::cout << "Entity destory by itself" << std::endl;
    }

    void print()
    {
        std::cout << "The value of x:" << x << ",y:" << y << std::endl;
    }
};

int main()
{
    std::shared_ptr<Entity> e;

    {
        std::shared_ptr<Entity> shared_entity = std::make_shared<Entity>(); // 性能缘由同上
        std::shared_ptr<Entity> shared_entity_new(new Entity(1, 2));

        e = shared_entity;
    }

    e->print();

    std::cout << "Program is going done~" << std::endl;
    return 0;
}

shared_ptr底层实现类似计数器,不分享的时候相当于在unique_ptr和计数器=0,分享一次就会计数器++。当计数器为0后,再结束生命周期就会delete这个对象本体(在堆上),否则只是释放了指针本身的内存(在栈上)。给weak_ptr分享不会增加计数器。

C++计算偏移量

计算偏移量在计算机图形学里比较多见,先写一下具体过程

#include <iostream>

struct Vector3
{
    float x, y, z;
};

int main()
{
    int offset = (int)&((Vector3*)nullptr)->y;
    
    std::cout << offset << std::endl;

    std::cin.get();
    return 0;
}

代码中offset就是偏移量。首先将0或者nullptr强制转化为结构体或类的指针,再去通过->调用指针就会根据偏移量偏移到目标的起始位置(这就是我们需要的值),之后取这个位置的地址(因为使用0或者nullptr作为起始地址所以不用再考虑起始地址)用&取地址(内存编号为偏移量),再通过(int)强制转换成整数就得到了偏移量

C++中优化std vector三个简单方法

我们先来写一段代码实验一下

#include <iostream>
#include <vector>

struct Vertex
{
    float x, y, z;

    Vertex(float x, float y, float z)
        : x(x), y(y), z(z)
    {
    }

    // copy constructor
    Vertex(const Vertex& vertex)
        : x(vertex.x), y(vertex.y), z(vertex.z)
    {
        std::cout << "Copied!" << std::endl;
    }
};

int main()
{
    std::vector<Vertex> vertices;
    vertices.push_back({ 1, 2, 3 }); //1号语句
    vertices.push_back({ 4, 5, 6 }); //2号语句
    vertices.push_back({ 7, 8, 9 }); //3号语句
    return 0;
}

这段代码中我们规定:Vertex结构体,每当被复制后就向控制台输出
经过实际运行后,这段代码一共输出了 6 行Copied,说明在添加到vector时Vertex被复制了六次。以下是流程:执行到1号语句时vertices的承载力为0,产生新的vertices并将旧的vertices复制到新的vertices中此时输出1次Copied,到二号语句,承载力不够,接着出现新的容器,旧的vertices中已经有的Vertex和新的Vertex都会被复制到新的容器中,此时复制了两次,即输出两次Copied,以此类推三号语句输出了三次Copied,最后即输出了1+2+3次即6次Copied
这段过程看似在计算机强大算力前不复杂,但是当push_back次数多了以后复制次数就会爆炸式增长

解决方案 一

先告诉vector给留多少空间,避免了多次复制

#include <iostream>
#include <vector>

struct Vertex
{
    float x, y, z;

    Vertex(float x, float y, float z)
        : x(x), y(y), z(z)
    {
    }

    // copy constructor
    Vertex(const Vertex& vertex)
        : x(vertex.x), y(vertex.y), z(vertex.z)
    {
        std::cout << "Copied!" << std::endl;
    }
};

int main()
{
    std::vector<Vertex> vertices;
    vertices.reserve(3);    // 预留空间
    vertices.push_back(Vertex(1, 2, 3));
    vertices.push_back(Vertex(4, 5, 6));
    vertices.push_back(Vertex(7, 8, 9));
    return 0;
}

在这里通过reserve告诉vector给我留3个单位的空间,运行完毕后仅输出了 3 次Copied,即没有新的容器创建

解决方案 二

通过调用类或者结构体的构造器,函数括号内为构造器对应参数

#include <iostream>
#include <vector>

struct Vertex
{
    float x, y, z;

    Vertex(float x, float y, float z)
        : x(x), y(y), z(z)
    {
    }

    // copy constructor
    Vertex(const Vertex& vertex)
        : x(vertex.x), y(vertex.y), z(vertex.z)
    {
        std::cout << "Copied!" << std::endl;
    }
};

int main()
{
    std::vector<Vertex> vertices;
    vertices.emplace_back(1, 2, 3);
    vertices.emplace_back(4, 5, 6);
    vertices.emplace_back(7, 8, 9);
    return 0;
}

此方法效率和法一相同,实现机制不同

解决方案 三(二 + 一)

预留好空间后再使用emplace不会产生任何复制(预留空间>=实际需求空间)

#include <iostream>
#include <vector>

struct Vertex
{
    float x, y, z;

    Vertex(float x, float y, float z)
        : x(x), y(y), z(z)
    {
    }

    // copy constructor
    Vertex(const Vertex& vertex)
        : x(vertex.x), y(vertex.y), z(vertex.z)
    {
        std::cout << "Copied!" << std::endl;
    }
};

int main()
{
    std::vector<Vertex> vertices;
    vertices.reserve(3);
    vertices.emplace_back(1, 2, 3);
    vertices.emplace_back(4, 5, 6);
    vertices.emplace_back(7, 8, 9);
    return 0;
}

此示例Vertex类没有发生复制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2181855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang | Leetcode Golang题解之第448题找到所有数组中消失的数字

题目&#xff1a; 题解&#xff1a; func findDisappearedNumbers(nums []int) (ans []int) {n : len(nums)for _, v : range nums {v (v - 1) % nnums[v] n}for i, v : range nums {if v < n {ans append(ans, i1)}}return }

OceanBase企业级分布式关系数据库

简介 OceanBase 数据库是阿里巴巴和蚂蚁集团不基于任何开源产品&#xff0c;完全自研的原生分布式关系数据库软件&#xff0c;在普通硬件上实现金融级高可用&#xff0c;首创“三地五中心”城市级故障自动无损容灾新标准&#xff0c;具备卓越的水平扩展能力&#xff0c;全球首…

使用微服务Spring Cloud集成Kafka实现异步通信(消费者)

1、本文架构 本文目标是使用微服务Spring Cloud集成Kafka实现异步通信。其中Kafka Server部署在Ubuntu虚拟机上&#xff0c;微服务部署在Windows 11系统上&#xff0c;Kafka Producer微服务和Kafka Consumer微服务分别注册到Eureka注册中心。Kafka Producer和Kafka Consumer之…

Ajax ( 是什么、URL、axios、HTTP、快速收集表单 )Day01

AJAX 一、Ajax是什么1.1名词解释1.1.1 服务器1.1.2 同步与异步1. 同步&#xff08;Synchronous&#xff09;2. 异步&#xff08;Asynchronous&#xff09;3. 异步 vs 同步 场景4. 异步在 Web 开发中的常见应用&#xff1a; 1.2 URL 统一资源定位符1.2.1 URL - 查询参数1.2.2 ax…

经典RCU锁原理及Linux内核实现

经典RCU锁原理及Linux内核实现 RCU锁原理 RCU锁第一个特点就是适用于读很多写很少的场景&#xff0c;那它和读写锁有什么区别呢&#xff1f;区别就是RCU锁读者完全不用加锁&#xff08;多个写者之间仍需要竞争锁&#xff09;&#xff0c;而读写锁&#xff08;不管是读优先、写…

https://www.aitoolpath.com/ 一个工具数据库,目前储存了有2000+各种工具。每日更新

AI 工具爆炸&#xff1f;别怕&#xff0c;这个网站帮你整理好了&#xff01; 哇塞&#xff0c;兄弟们&#xff01;AI 时代真的来了&#xff01;现在各种 AI 工具跟雨后春笋似的&#xff0c;噌噌噌地往外冒。AI 写作、AI 绘画、AI 代码生成……简直是要逆天啊&#xff01; 可是…

XSS | XSS 常用语句以及绕过思路

关注这个漏洞的其他相关笔记&#xff1a;XSS 漏洞 - 学习手册-CSDN博客 0x01&#xff1a;干货 - XSS 测试常用标签语句 0x0101&#xff1a;<a> 标签 <!-- 点击链接触发 - JavaScript 伪协议 --><a hrefjavascript:console.log(1)>XSS1</a> <!-- 字…

智能制造--EAP设备自动化程序

EAP是设备自动化程序&#xff08;Equipment Automation Program&#xff09;的缩写&#xff0c;他是一种用于控制制造设备进行自动化生产的系统。EAP系统与MES系统整合&#xff0c;校验产品信息&#xff0c;自动做账&#xff0c;同时收集产品生产过程中的制程数据和设备参数数据…

Spring MVC__入门

目录 一、SpringMVC简介1、什么是MVC2、什么是SpringMVC 二、Spring MVC实现原理2.1核心组件2.2工作流程 三、helloworld1、开发环境2、创建maven工程3、配置web.xml4、创建请求控制器5、创建springMVC的配置文件6、测试HelloWorld7、总结 一、SpringMVC简介 1、什么是MVC MV…

git 报错git: ‘remote-https‘ is not a git command. See ‘git --help‘.

报错内容 原因与解决方案 第一种情况&#xff1a;git路径错误 第一种很好解决&#xff0c;在环境变量中配置正确的git路径即可&#xff1b; 第二种情况 git缺少依赖 这个情况&#xff0c;网上提供了多种解决方案。但如果比较懒&#xff0c;可以直接把仓库地址的https改成ht…

【Kotlin基于selenium实现自动化测试】初识selenium以及搭建项目基本骨架(1)

导读大纲 1.1 Java: Selenium 首选语言1.2 配置一个强大的开发环境 1.1 Java: Selenium 首选语言 Java 是开发人员和测试人员进行自动化 Web 测试的首选 Java 和 Selenium 之间的协同作用受到各种因素的驱动,从而提高它们的有效性 为什么Java经常被认为是Selenium的首选语言 广…

记录一次出现循环依赖问题

具体的结构设计&#xff1a; 在上面的图片中&#xff1a; UnboundBlackVerifyChain类中继承了UnboundChain类。但是UnboundChain类中注入了下面三个类。 Scope(“prototype”) UnboundLinkFlowCheck类 Scope(“prototype”) UnboundUserNameCheck类 Scope(“prototype”) Un…

[linux 驱动]input输入子系统详解与实战

目录 1 描述 2 结构体 2.1 input_class 2.2 input_dev 2.4 input_event 2.4 input_dev_type 3 input接口 3.1 input_allocate_device 3.2 input_free_device 3.3 input_register_device 3.4 input_unregister_device 3.5 input_event 3.6 input_sync 3.7 input_se…

用网络分析仪测试功分器驻波的5个步骤

在射频系统中&#xff0c;功分器的驻波比直接关系到信号的稳定性和传输效率。本文将带您深入了解驻波比的测试方法和影响其结果的因素。 一、功分器驻波比 驻波(Voltage Standing Wave Ratio)&#xff0c;简称SWR或VSWR&#xff0c;是指频率相同、传输方向相反的两种波&#xf…

.NET Core 高性能并发编程

一、高性能大并发架构设计 .NET Core 是一个高性能、可扩展的开发框架&#xff0c;可以用于构建各种类型的应用程序&#xff0c;包括高性能大并发应用程序。为了设计和开发高性能大并发 .NET Core 应用程序&#xff0c;需要考虑以下几个方面&#xff1a; 1. 异步编程 异步编程…

最大正方形 Python题解

最大正方形 题目描述 在一个 n m n\times m nm 的只包含 0 0 0 和 1 1 1 的矩阵里找出一个不包含 0 0 0 的最大正方形&#xff0c;输出边长。 输入格式 输入文件第一行为两个整数 n , m ( 1 ≤ n , m ≤ 100 ) n,m(1\leq n,m\leq 100) n,m(1≤n,m≤100)&#xff0c;接…

养猪场饲料加工机械设备有哪些

养猪场饲料加工机械设备主要包括以下几类&#xff1a;1‌、粉碎机‌&#xff1a;主要用于将原料进行粉碎&#xff0c;以便与其他饲料原料混合均匀。常见的粉碎机有水滴式粉碎机和立式粉碎机两种&#xff0c;用户可以根据原料的特性选择适合的机型。2‌、搅拌机‌&#xff1a;用…

ONVIF、GB28181技术特点和使用场景分析

技术背景 好多开发者希望搞明白ONVIF和GB28181的区别和各自适合的场景&#xff0c;为什么大牛直播SDK只做了GB28181接入端&#xff0c;没有做ONVIF&#xff1f;本文就二者差别&#xff0c;做个大概的介绍。 ONVIF ONVIF&#xff08;Open Network Video Interface Forum&…

【Linux 23】线程池

文章目录 &#x1f308; 一、线程池的概念&#x1f308; 二、线程池的应用场景&#x1f308; 三、线程池的实现 &#x1f308; 一、线程池的概念 线程池 (thread pool) 是一种利用池化技术的线程使用模式。 虽然创建线程的代价比创建进程的要小很多&#xff0c;但小并不意味着…

Mysql高级篇(下)——日志

日志 一、日志概述二、日志弊端二、日志分类三、 各日志详情介绍1、慢查询日志&#xff08;Slow Query Log&#xff09;2、通用查询日志&#xff08;General Query Log&#xff09;3、错误日志&#xff08;Error Log&#xff09;4、二进制日志&#xff08;Binary Log&#xff0…