C++小知识

最近找实习也在同步复习,以前只学习不思考,知识点这从没想过拓展,这知识点一复习才发现居然有这么多以前听都没听过的(有可能是我忘了),今天写出来给大家看看,如果有人也跟我一样没在意过这些,那你可算是捡到了

1.  局部变量屏蔽:

在函数中,如果局部变量和全局变量名字相同,那么在函数内部局部变量会屏蔽掉全局变量,也就是这个名字代表了局部变量,如果想使用全局变量,在名字前加作用域运算符:: ,这个运算符不能用于访问局部变量

2.  指针

在32位环境,不论任何类型指针大小都是4字节,64位环境是8字节,他们的大小都是相同的,区别在于指针跨步大小不同,不同类型的指针的跨步大小和他们的类型一样大

3.  字符串和字符数组

使用string类型定义字符串后可以使用=来赋值

string str1;
str1="hello"

但是使用char定义字符数组不能这样做

char str2[10];
str2="world";//错误,表达式左侧必须是可以修改的左值

因为字符串数组是常量指针,不能被修改,想要给它赋值应该使用strcpy函数

char str2[10];
strcpy(str2,"world");

并且注意字符串大小不应该小于7(world!\0)否则提示栈被破坏

但是字符串常量不存在这种问题,可以直接赋值,也可以通过字符串给另一个字符串赋值,并不用在意大小

cin(标准输入可以直接写入字符数组)

#include <iostream>
#include <cstring>
using namespace std;
int main() {   
    char str1[10];
    char str2[12];
    cin >> str1;
    strcpy(str2, str1);
    cout << str2;
    return 0;
}

字符串之间可以通过关系运算符进行比较,比较大小的规则是,从左到右每一位按照ascll码大小比较例如:banana>apple,因为b(98)>a(97)

    4.类和结构体

类和结构体名字相同提示重定义,因为c++中 结构体就是类唯一区别在于默认访问权限类默认私有,结构体默认公有,在c++中创建一个结构体和创建一个类是一样的

5.  成员函数不占用内存

6.  库函数minmax_element(力扣14)

这是做题的时候看见的一个题解里提到的,以前没注意过这些函数居然这么好用,minmax_element 是 C++ 标准库 <algorithm> 中的函数,用于在指定范围内找到最小值和最大值的迭代器。这个函数返回一对迭代器(minmax变量被auto推导为迭代器),第一个迭代器指向范围内的最小元素,而第二个迭代器指向范围内的最大元素。

下面是 minmax_element 函数的基本用法示例:

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    std::vector<int> numbers = {4, 2, 7, 5, 9, 1, 6};

    auto minmax = std::minmax_element(numbers.begin(), numbers.end());

    std::cout << "Min element: " << *minmax.first << std::endl;
    std::cout << "Max element: " << *minmax.second << std::endl;
//输出1和9
    return 0;
}

在这个示例中,minmax_element 函数被用来找到 numbers 向量中的最小值和最大值。然后,通过 minmax.firstminmax.second 访问找到的最小值和最大值。

minmax_element 的时间复杂度为 O(n),其中 n 是指定范围内的元素数量。

7.  字符串比较

在 C++ 中,字符串的比较操作通常是比较字符串的内容(字典序 每一位的ascll表顺序)。具体来说,比较操作会逐个比较字符串中的字符,直到找到不同的字符或者一个字符串的字符全部比较完毕为止。

常见的比较操作包括:

1. 等于比较(==):比较两个字符串的内容是否完全相同。

2. 不等于比较(!=):比较两个字符串的内容是否不同。

3. 大于比较(>):按字典序比较两个字符串,如果第一个字符串在字典序中排在第二个字符串之后,则返回 true。

4. 小于比较(<):按字典序比较两个字符串,如果第一个字符串在字典序中排在第二个字符串之前,则返回 true。

5. 大于等于比较(>=):比较两个字符串的字典序,如果第一个字符串在字典序中排在第二个字符串之后或者两个字符串相等,则返回 true。

6. 小于等于比较(<=):比较两个字符串的字典序,如果第一个字符串在字典序中排在第二个字符串之前或者两个字符串相等,则返回 true。

这些比较操作可以帮助我们在编程中对字符串进行大小关系的判断,从而进行相应的逻辑控制或排序等操作。

8.库函数substr(截取字符串)

substr 是 C++ 标准库 <string> 中的成员函数,用于提取字符串的子串。它接受两个参数,第一个参数是子串的起始位置(索引),第二个参数是子串的长度(可选,如果省略第二个参数,则提取从起始位置到字符串末尾的所有字符作为子串)。

下面是 substr 函数的基本用法示例:

#include <iostream>
#include <string>

int main() {
    std::string str = "Hello, World!";
    
    // 提取从索引为 7 开始的子串
    std::string substr1 = str.substr(7);
    std::cout << "Substring 1: " << substr1 << std::endl; // 输出 "World!"

    // 提取从索引为 0 开始长度为 5 的子串
    std::string substr2 = str.substr(0, 5);
    std::cout << "Substring 2: " << substr2 << std::endl; // 输出 "Hello"

    return 0;
}

在这个示例中,substr 函数被用来从字符串 str 中提取子串,并存储到新的字符串变量中。第一个 substr 调用提取了从索引为 7 开始的子串(包括索引为 7 的字符),第二个 substr 调用提取了从索引为 0 开始长度为 5 的子串。

需要注意的是,substr 函数返回的是一个新的 std::string 对象,表示提取出的子串。

9.  程序局部性

程序局部性原理计算机科学中的一个基本概念,它描述了程序在执行时的一个特性,即程序在执行时呈现出局部性规律。这种局部性表现在两个方面:时间局部性和空间局部性。

1.时间局部性指的是如果程序中的某条指令一旦执行,则不久之后该指令可能再次被执行;如果某数据被访问,则不久之后该数据可能再次被访问。这种局部性通常是由于程序中存在大量的循环操作所导致的。

空间局部性则是指一旦程序访问了某个存储单元,则不久之后其附近的存储单元也将被访问。这种局部性是由于程序顺序执行的特点以及数据结构(如数组)的连续存放所导致的。

程序的局部性原理对计算机系统的设计和性能优化有着重要的影响。例如,在虚拟存储技术中,程序的局部性原理是引入的前提,它允许系统只将当前最活跃的代码和数据段加载到内存中,而将不活跃的部分留在磁盘上,从而提高了内存的利用率和系统的性能。此外,现代计算机系统的各个层次,从硬件操作系统应用程序等,在设计上都利用了局部性原理,如缓存机制CPU指令顺序处理等。

虚拟内存和分页内存

虚拟内存的原理是让程序认为自己所使用的内存是连续的 但是实际上内存是分散的或者存储在外部存储器上的 只在需要时进行数据交互,内存分页和虚拟内存的原理对应 即可以使进程的物理地址是不连续的 使用PTE(一个虚拟内存到物理内存的映射关系)寻址使进程运行,单级页表的问题:虽然能够做到1:1024比例的存储,但是其真实物理地址必须连续,当进程足够大,足够多时,页表就会造成相当大的空间浪费(因为页表中存储PTE本质就是个数组,所以必须连续)

10.  左值右值

左值:可以赋值的变量,可以获取地址的变量,可以出现在等号左侧和右侧

int*p=new int(0);
a=10;//a才是左值
int b=a;

右值:一个表示数据的表达式,如函数返回值,表达式返回值,不可以取地址,只能出现在等号右侧

min(5,10);//如果写为a=min(5,10),则a为左值,因为被赋值了
10//常量无法修改和取地址,所以常量为右值

引用:

左值只能引用左值,但是const左值可以引用左值和右值(因为此时不可改变,和右值定义相同,所以可以引用)

右值只可以引用右值,但是左值使用move(左值)函数转化为右值后也可以被引用

int p=10;
int&a=p;//可以使用,因为P为左值,此时为左值引用左值
int&b=10;//不可以使用,因为10为右值,此时为左值引用右值,编译器报错

const int&c=p;//可以使用,此时为const左值引用左值
const int&d=10//也可以使用,此时为const左值引用右值


右值引用的优势在于,左值引用会产生一个副本,而右值引用拥有移动语义的能力,也就是动态的将参数移动过去,不会产生副本,这个移动的过程挺形象,右值作为临时对象,将自己的内容复制给新对象后自己就会消失,而新对象和原对象一模一样,看起来就像移动过去一样,他的作用就是避免使用深拷贝,因为深拷贝会产生副本,成本太高

右值不可以修改!

11=min(5,10);//报错,右值不可以修改
//但是 当右值引用右值,右值就变成了左值
int&&p=10;//右值引用
p++;//此时p为11,因为p此时变为了左值,可以修改

const右值引用右值引用右值之后,被引用的右值就会被修改,也就是它变成了左值(本质还是右值,但是表现为左值),则每一次改变之后都需要move左值,但是我们希望不这么麻烦 引出了可以自动识别类型的万能引用

11.  万能引用

1.参数类型推导:函数传参数时例如

aaa(T&&t);
//由于不清楚T的类型,不知道他是左值还是右值,所以会根据传入的实参推导T
//的类型

2.引用折叠:推导出T的类型后会保持他的类型,如果被推导为左值引用,会折叠成左值引用类型;如果推导为右值引用,就会保持为右值引用类型。

万能引用的主要用途是在泛型编程中保持参数的完整类型信息,并将其传递到其他函数中,同时保留原始调用时的值类别(左值或右值),从而实现高效的转发,避免不必要的对象拷贝或移动操作。这种特性尤其在实现通用库时,如标准库中的std::forward函数中被广泛应用

12.  完美转发

在C++中,完美转发(Perfect Forwarding)是一种技术,它允许函数模板将其实参的值完美地转发给另一个函数,包括值的类别(左值或右值)和const/volatile限定。完美转发通常用于模板函数中,以确保实参的值能够以它们被传递时的原始形式传递给其他函数

完美转发的关键在于使用万能引用(Universal Reference),它是一种特殊的引用类型,可以用于模板参数。万能引用是模板参数T的右值引用T&&,当它作为模板参数时,可以推导为左值引用或右值引用,具体取决于传递给它的实参的值类别。

下面是一个使用完美转发的简单示例:

template <typename T>
void forwarder(T&& arg) {
    // 使用std::forward<T>(arg)来实现完美转发
    someOtherFunction(std::forward<T>(arg));
}

void someOtherFunction(int& x) {
    // 处理左值
    std::cout << "Lvalue\n";
}

void someOtherFunction(int&& x) {
    // 处理右值
    std::cout << "Rvalue\n";
}

int main() {
    int a = 10;
    forwarder(a); // 完美转发a作为左值
    forwarder(10); // 完美转发字面值10作为右值
    return 0;
}

在上面的例子中,forwarder函数模板接受一个万能引用参数arg。在forwarder函数中,使用std::forward<T>(arg)来转发arg给someOtherFunction。std::forward会根据arg的原始值类别(左值或右值)来保持它的引用类型,从而实现完美转发。

当forwarder函数被调用时,无论传入的是左值还是右值,std::forward都会确保someOtherFunction接收到的参数类型和值类别与传入forwarder的参数一致。这样,someOtherFunction就可以根据传入参数的值类别来决定如何处理它

完美转发的出现是为了防止参数的状态改变,参数在什么情况下会发生改变呢?主要有两个关键情况:

传递给函数的参数是右值:如果参数是右值,如字面值或临时对象,通过完美转发,它仍然可以作为右值传递给下一个函数。这对于移动语义和性能优化非常重要,因为它可以避免不必要的拷贝操作。

传递给函数的参数是左值:如果参数是左值,如变量或表达式的结果,通过完美转发,它可以作为左值传递给下一个函数。这使得函数可以修改或使用该参数的值,而不会影响到原始调用的值。

14.foreach循环(增强for循环)

这个以前其实学过,但是很少使用就给遗忘了,昨天的面试题里提到了这个我才想起来原来还有这么个东西,在C++中,for循环参数中的:表示范围-based for循环,也称为foreach循环。它是一种简化遍历容器元素的语法结构,通常用于遍历数组、容器、字符串等。

语法形式如下

for (auto element : container) {
    // 使用element进行存储
}

其中,auto element 是声明一个变量 element,它会依次获取container中的每一个元素,类型会自动推导为容器元素的类型。container 可以是任何支持迭代器的数据结构,如数组、vector、set、string等。

例如,遍历一个vector的例子:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    for (auto num : vec) {
        std::cout << num << " ";
    }
    
    return 0;
}

上述代码会输出:1 2 3 4 5 ,每个元素用空格分隔。

这种for循环形式相比传统的索引方式更加简洁和易读,并且避免了手动管理索引变量的问题,因此在C++中被广泛使用。

15.死锁

这个我之前的文章里提到了,就不讲了

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

void* thread1(void* arg) {
    pthread_mutex_lock(&mutex1);
    printf("Thread 1 acquired mutex1\n");
    sleep(1);
    pthread_mutex_lock(&mutex2);
    printf("Thread 1 acquired mutex2\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

void* thread2(void* arg) {
    pthread_mutex_lock(&mutex2);
    printf("Thread 2 acquired mutex2\n");
    sleep(1);
    pthread_mutex_lock(&mutex1);
    printf("Thread 2 acquired mutex1\n");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex2);
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread1, NULL);
    pthread_create(&tid2, NULL, thread2, NULL);
    
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

16.  auto

auto 关键字在 C++11 标准中被引入,用于创建自动类型推断的变量。使用 auto 关键字声明的变量,其类型由初始化表达式自动推断得出。

例如,以下代码段中,auto 关键字会根据等号右边的表达式来推断出变量的类型:

auto x = 10; // 推断出 x 的类型为 int
auto y = 3.14; // 推断出 y 的类型为 double
auto z = 'a'; // 推断出 z 的类型为 char
auto w = "Hello, World!"; // 推断出 w 的类型为 const char*
在 C++14 及以后的版本中,auto 还可以用于推断函数模板参数的类型或者 lambda 表达式的返回类型。

使用 auto 关键字可以减少代码中的重复类型声明,提高代码的可读性和简洁性。同时,它允许程序员编写更加泛型的代码,因为编译器会根据上下文自动推断出适当的类型。

别的没啥,这里的右值引用内存分页部分我整理的比较浅显,但是够用了,如果更想了解那就看看别人的文献吧,个人整理熬,有错就提

雷子保佑我成功

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值