• [技术干货] C++ 在类中创建并使用线程
    thread库在C++中,你可以使用<thread>库来在类中创建线程。然而,直接在类的成员函数中启动线程可能不是一个好主意,因为线程的生命周期和对象的生命周期可能会变得难以管理。但你可以通过在类的成员函数或成员变量中封装线程创建的逻辑来实现这一功能。简单线程示例以下是一个简单的示例,展示了如何在C++类的成员函数中启动一个线程:#include <iostream> #include <thread> #include <chrono> class MyClass { public: MyClass() : threadPtr(nullptr) {} // 启动线程的成员函数 void startThread() { // 检查线程是否已经在运行 if (threadPtr == nullptr) { // 使用lambda表达式捕获this指针,以便在线程中访问类的成员 threadPtr = std::make_unique<std::thread>([this]() { this->runInThread(); }); } else { std::cout << "Thread is already running!" << std::endl; } } // 线程执行的函数 void runInThread() { for (int i = 0; i < 5; ++i) { std::cout << "Running in thread: " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } } // 等待线程完成 void joinThread() { if (threadPtr != nullptr && threadPtr->joinable()) { threadPtr->join(); threadPtr.reset(); // 释放线程对象 } } private: std::unique_ptr<std::thread> threadPtr; // 使用unique_ptr管理线程对象的生命周期 }; int main() { MyClass obj; obj.startThread(); // 启动线程 // 让主线程等待一段时间,以便观察子线程的输出 std::this_thread::sleep_for(std::chrono::seconds(6)); obj.joinThread(); // 等待线程完成 return 0; }在这个示例中,MyClass类有一个成员变量threadPtr,它是一个std::unique_ptr<std::thread>类型,用于管理线程对象的生命周期。startThread成员函数检查线程是否已经在运行,如果没有,则创建一个新线程来执行runInThread成员函数。joinThread成员函数用于等待线程完成并释放线程对象。注意,在捕获this指针时要小心,因为如果对象在线程执行期间被销毁,这可能会导致未定义的行为。在这个示例中,我们通过使用std::unique_ptr来确保线程对象在线程完成后被正确销毁。线程传参如果你需要在startThread函数中传递一些参数到线程执行的函数中,你可以通过几种方式来实现。以下是一个示例,展示了如何使用std::bind或lambda表达式来传递参数:使用std::bind#include <iostream> #include <thread> #include <chrono> #include <functional> class MyClass { public: MyClass() : threadPtr(nullptr) {} // 启动线程的成员函数,接受一个int参数 void startThread(int param) { // 检查线程是否已经在运行 if (threadPtr == nullptr) { // 使用std::bind将参数绑定到成员函数上 threadPtr = std::make_unique<std::thread>(std::bind(&MyClass::runInThread, this, param)); } else { std::cout << "Thread is already running!" << std::endl; } } // 线程执行的函数,接受一个int参数 void runInThread(int param) { for (int i = 0; i < 5; ++i) { std::cout << "Running in thread with param: " << param << ", count: " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } } // 等待线程完成 void joinThread() { if (threadPtr != nullptr && threadPtr->joinable()) { threadPtr->join(); threadPtr.reset(); // 释放线程对象 } } private: std::unique_ptr<std::thread> threadPtr; // 使用unique_ptr管理线程对象的生命周期 }; int main() { MyClass obj; obj.startThread(42); // 启动线程并传递参数42 // 让主线程等待一段时间,以便观察子线程的输出 std::this_thread::sleep_for(std::chrono::seconds(6)); obj.joinThread(); // 等待线程完成 return 0; }使用Lambda表达式// ... 省略其他部分 ... void startThread(int param) { // 检查线程是否已经在运行 if (threadPtr == nullptr) { // 使用lambda表达式捕获this指针和param参数 threadPtr = std::make_unique<std::thread>([this, param]() { this->runInThread(param); }); } else { std::cout << "Thread is already running!" << std::endl; } } // ... 省略其他部分 ... int main() { MyClass obj; obj.startThread(42); // 启动线程并传递参数42 // ... 省略其他部分 ... }在这两个示例中,我们都修改了startThread函数以接受一个int参数,并将该参数传递给线程执行的函数runInThread。在第一个示例中,我们使用std::bind来绑定参数和成员函数,而在第二个示例中,我们使用lambda表达式来捕获参数并调用成员函数。两种方法都可以实现相同的效果。
  • [问题求助] 【MDC300】在mini0上运行编译生成文件报错
    运行了MDC300sample-1.0.107.2中的Ascendcl_sample,模型推理和编译都没有问题,把最终生成的编译文件bin,模型文件model,图片image发到mini上面运行,结果报错。
  • [问题求助] iot studio软件按照步骤安装,编译显示Arm-none-eabi-gcc:致命错误:没有输入文件编译终止。
    iot studio软件按照步骤安装,编译显示Arm-none-eabi-gcc:致命错误:没有输入文件编译终止。
  • [常见FAQ] 选手程序输出异常
    本地和判题器交互正常,有最终得分。但是线上提交显示:选手程序输出异常。
  • [技术干货] 64位MFC调用32位DLL
    当64位MFC应用程序调用32位DLL时,你可以通过创建一个中间层Wrapper DLL来实现。下面是一个简单的示例,演示如何从64位MFC应用程序调用32位DLL的函数:假设你有一个32位DLL,其中包含一个名为MyFunction的函数,它接受一个整数参数并返回一个整数。你想从64位MFC应用程序中调用这个函数。首先,创建一个新的64位DLL项目(Wrapper DLL),用于与32位DLL进行交互。在这个64位DLL项目中,编写一个导出函数,该函数将调用32位DLL的函数。下面是一个简单的例子:// WrapperDLL.h #pragma once #ifdef WRAPPERDLL_EXPORTS #define WRAPPERDLL_API __declspec(dllexport) #else #define WRAPPERDLL_API __declspec(dllimport) #endif extern "C" WRAPPERDLL_API int CallMyFunction(int value);// WrapperDLL.cpp #include "WrapperDLL.h" #include "windows.h" typedef int(*MYFUNCTION)(int); int CallMyFunction(int value) { HINSTANCE hDLL = LoadLibrary(L"path_to_32bit_dll.dll"); if (hDLL != NULL) { MYFUNCTION myFunction = (MYFUNCTION)GetProcAddress(hDLL, "MyFunction"); if (myFunction != NULL) { int result = myFunction(value); FreeLibrary(hDLL); return result; } FreeLibrary(hDLL); } return -1; }在这个示例中,我们创建了一个名为CallMyFunction的导出函数。该函数加载32位DLL,并使用GetProcAddress函数获取MyFunction函数的地址。然后,我们将参数传递给32位DLL的函数,并返回结果。接下来,编译这个Wrapper DLL项目,并生成一个64位的DLL文件。然后,在你的64位MFC应用程序中,你可以通过调用CallMyFunction函数来间接调用32位DLL的函数。例如:// MFCAppDlg.cpp (64位MFC应用程序的对话框类文件) #include "WrapperDLL.h" // ... void CMFCAppDlg::OnBnClickedButton1() { int result = CallMyFunction(42); // 处理结果... }这里,我们在MFC应用程序中的按钮单击事件处理程序中调用了CallMyFunction函数,将参数值 42 传递给32位DLL的函数。你可以根据需要使用返回的结果。需要确保在编译64位MFC应用程序和Wrapper DLL时,使用相应的64位编译器选项。此外,还需要将32位DLL的路径替换为你自己的实际路径。这只是一个简单的示例,具体的实现可能因实际情况而有所不同。你可能需要根据32位DLL的特定函数和参数来进行更多的调整和适配。
  • [常见FAQ] C++如何Debug
    文档里说的调试器太简单了   能细说吗?解锁新知识
  • [问题求助] 请求文档链接
    请问谁有《IPC V200R003C00 智能元数据结构设计说明书》?谢谢!
  • [技术干货] 【c++百日刷题计划】 ———— DAY9,奋战百天,带你熟练掌握基本算法-转载
    第一题 集合求和题目描述给定一个集合 s ss(集合元素数量 ≤ 30 \le 30≤30),求出此集合所有子集元素之和。输入格式集合中的元素(元素 ≤ 1000 \le 1000≤1000)输出格式s ss 所有子集元素之和。样例 #1样例输入 #12 31样例输出 #1101提示【样例解释】子集为:∅ , { 2 } , { 3 } , { 2 , 3 } \varnothing, \{ 2 \}, \{ 3 \}, \{ 2, 3 \}∅,{2},{3},{2,3},和为 2 + 3 + 2 + 3 = 10 2 + 3 + 2 + 3 = 102+3+2+3=10。【数据范围】对于 100 % 100 \%100% 的数据,1 ≤ ∣ s ∣ ≤ 30 1 \le \lvert s \rvert \le 301≤∣s∣≤30,1 ≤ s i ≤ 1000 1 \le s_i \le 10001≤s i​ ≤1000,s ss 所有子集元素之和 ≤ 10 18 \le {10}^{18}≤10 18 。解题思路1)数学推理题目。2)找规律发现,每个元素在 a 的每一个子集中出现的个数为2 n − 1 {2}^{n-1}2 n−1 。参考代码#include<bits/stdc++.h>using namespace std;int a[31];long long s=0;int main(){    int n=0;     {        s+=a[i];    }    s*=pow(2,n-2);    cout<<s;    return 0;}123456789101112131415第二题 [NOIP2004 普及组] 火星人题目描述人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。火星人用一种非常简单的方式来表示数字――掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为 1 , 2 , 3 , ⋯ 1,2,3,\cdots1,2,3,⋯。火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。一个火星人用一个人类的手演示了如何用手指计数。如果把五根手指――拇指、食指、中指、无名指和小指分别编号为 1 , 2 , 3 , 4 1,2,3,41,2,3,4 和 5 55,当它们按正常顺序排列时,形成了 5 55 位数 12345 1234512345,当你交换无名指和小指的位置时,会形成 5 55 位数 12354 1235412354,当你把五个手指的顺序完全颠倒时,会形成 54321 5432154321,在所有能够形成的 120 120120 个 5 55 位数中,12345 1234512345 最小,它表示 1 11;12354 1235412354 第二小,它表示 2 22;54321 5432154321 最大,它表示 120 120120。下表展示了只有 3 33 根手指时能够形成的 6 66 个 3 33 位数和它们代表的数字:三进制数    代表的数字123 123123    1 11132 132132    2 22213 213213    3 33231 231231    4 44312 312312    5 55321 321321    6 66现在你有幸成为了第一个和火星人交流的地球人。一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。你的任务是,把火星人用手指表示的数与科学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。输入数据保证这个结果不会超出火星人手指能表示的范围。输入格式共三行。第一行一个正整数 N NN,表示火星人手指的数目(1 ≤ N ≤ 10000 1 \le N \le 100001≤N≤10000)。第二行是一个正整数 M MM,表示要加上去的小整数(1 ≤ M ≤ 100 1 \le M \le 1001≤M≤100)。下一行是 1 11 到 N NN 这 N NN 个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。输出格式N NN 个整数,表示改变后的火星人手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。样例 #1样例输入 #1531 2 3 4 5123样例输出 #11 2 4 5 31提示对于 30 % 30\%30% 的数据,N ≤ 15 N \le 15N≤15。对于 60 % 60\%60% 的数据,N ≤ 50 N \le 50N≤50。对于 100 % 100\%100% 的数据,N ≤ 10000 N \le 10000N≤10000。解题思路1)模拟深度优先搜索的全排列问题参考代码#include<bits/stdc++.h>using namespace std;int a[10005];bool used[10005]={0};int m,n;int main(){    cin>>n>>m;    for(int i=1;i<=n;i++)    {        cin>>a[i];        int x=a[i];        for(int j=1;j<=a[i];j++)            x-=used[j];        used[a[i]]=1;        a[i]=x-1;    }    a[n]+=m;    for(int i=n;i>0;i--)    {        a[i-1]+=a[i]/(n-i+1);        a[i]%=n-i+1;    }    memset(used,0,sizeof(used));    for(int i=1;i<=n;i++)    {        for(int j=0;j<=a[i];j++)            if(used[j])                a[i]++;        cout<<a[i]+1<<" ";        used[a[i]]=1;    }    return 0;}12345678910111213141516171819202122232425262728293031323334第三题 [NOIP2004 普及组] 最大约数和题目描述选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。输入格式输入一个正整数S。输出格式输出最大的约数之和。样例 #1样例输入 #1111样例输出 #191提示样例说明取数字4和6,可以得到最大值(1+2)+(1+2+3)=9。数据规模S<=10001解题思路1)对数据进行预处理,求出每个数的约数和。2)简单的01背包问题。参考代码#include<bits/stdc++.h>using namespace std;int i,j,n,a[1001],dp[1001];int main(){    cin>>n;    for(i=1;i<=n/2;i++)        for(j=2;i*j<=n;j++)            a[i*j]+=i;    for(i=1;i<=n;i++)        for(j=i;j<=n;j++)            dp[j]=max(dp[j],dp[j-i]+a[i]);    cout<<dp[n];      return 0;}123456789101112131415第四题 [NOIP2004 普及组] 通天之分组背包题目背景直达通天路·小 A 历险记第二篇题目描述自 01 0101 背包问世之后,小 A 对此深感兴趣。一天,小 A 去远游,却发现他的背包不同于 01 0101 背包,他的物品大致可分为 k kk 组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。输入格式两个数 m , n m,nm,n,表示一共有 n nn 件物品,总重量为 m mm。接下来 n nn 行,每行 3 33 个数 a i , b i , c i a_i,b_i,c_ia i​ ,b i​ ,c i​ ,表示物品的重量,利用价值,所属组数。输出格式一个数,最大的利用价值。样例 #1样例输入 #145 310 10 110 5 150 400 21234样例输出 #1101提示1 ≤ m , n ≤ 1000 1 \leq m, n \leq 10001≤m,n≤1000。解题思路1)分组背包问题。2)直接枚举每个组,再在每个组里进行01背包。参考代码#include<bits/stdc++.h>using namespace std;long long dp[1050];int a[1050],b[1050],c,C[1050];int g[205][205];int main(){    int m,n;    cin>>m>>n;    int zu=0;    for(int i=1;i<=n;i++)    {                cin>>a[i]>>b[i]>>c;        zu=max(zu,c);        C[c]++;        g[c][C[c]]=i;    }        for(int i=1;i<=zu;i++)    {        for(int j=m;j>=0;j--)        {            for(int k=1;k<=C[i];k++)            {                if(j>=a[g[i][k]])                {                    dp[j]=max(dp[j],dp[j-a[g[i][k]]]+b[g[i][k]]);                }            }        }    }    cout<<dp[m];    return 0;}————————————————版权声明:本文为CSDN博主「锡兰_CC」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/Ceylan__/article/details/126246543
  • [技术干货] C/C++后端实习经验大礼包-转载
    C/C++后端实习经验大礼包文章目录C/C++后端实习经验大礼包一、前言二、实习面试宝典1.自我介绍2.深挖个人项目(多个问题不同角度)3.有用过STL吗?常用哪些STL?4.介绍一下你对STL的理解5.深挖STL底层6.Qt有接触过吗?Qt下Tcp通信的整个流程是怎么样的?7.设计模式的问题8.编译原理的简单问题9.讲一下dijkstra算法10.谈一下你对STL的理解11.STL的使用场景12.C++11语法熟悉吗13.工程里面的回溯是什么?14.Widget::Widget(QWidget *parent) :QWidget(parent)什么意思15.算法题:跳台阶(找规律可以发现是个斐波那契数列)16.算法题: 餐厅(区间贪心)17.反问面试官环节三、实习工作分享1.实习专有名词及分析篇1.1SDK:软件开发工具包(Software Development Kit)1.2API:应用程序接口(Application Programming Interface)举例:windows串口通信函数API1.3上位机1.4下位机1.5上位机与下位机区别四、总结一、前言这篇文章是C/C++后端实习经验大礼包的第一弹,后面会持续更新,谢谢大家支持~~撒花,前面说过博主目前在一家互联网公司实习,今天给大家带来第一弹C/C++后端实习经验大礼包,希望大家喜欢。123二、实习面试宝典流程:投简历->简历筛选->笔试->技术电话面->技术视频面->hr面(有些公司可能在中间环节比较多,比如两次技术电话面,两次技术视频面等,视频面多为主管之类的,ps:主管面会参考之前的部门对你面试的评价)1.自我介绍2.深挖个人项目(多个问题不同角度)3.有用过STL吗?常用哪些STL?回答:用过,平时用的比较多,常用vector,map,unordered_map,stack,queue,deque,set,multiset,unordered_set,priority_queue,bitset,list之类的4.介绍一下你对STL的理解回答:STL就是标准模板库,可以提高程序的开发效率和复用性。5.深挖STL底层vector:底层存储是一个可变大小的数组,支持O(1)的随机访问,在尾部之外的位置插入和删除操作时间复杂度是 O(n);deque:双端队列,支持O(1)随机访问在头部和尾部之外的位置插入和删除的时间复杂度是O(n);list: 双向链表,不支持随机访问,只支持顺序访问,在任意位置的插入和删除速度很快;forward_list:单向链表;array是固定大小的数组(vector是可变大小的数组);string:与vector类似,可以理解为特殊的vector,专门用来存储字符,支持随机访问,在尾部之外的位置插入的时间复杂度是O(n);6.Qt有接触过吗?Qt下Tcp通信的整个流程是怎么样的?有的,主要从服务器端和客户端两方面介绍:1.服务器端创建用于监听的套接字(Socket)Socket可以看成在两个程序进行通讯连接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。给套接字设置监听如果有连接到来, 监听的套接字会发出信号newConnected接收连接, 通过nextPendingConnection()函数, 返回一个QTcpSocket类型的套接字对象(用于通信)使用用于通信的套接字对象通信1 发送数据: write2. 接收数据: readAll/read客户端方面:创建用于通信的套接字连接服务器: connectToHost连接成功与服务器通信1 发送数据: write2.接收数据: readAll/read7.设计模式的问题后面我会专门写文章来讲8.编译原理的简单问题后面我会专门写文章来讲9.讲一下dijkstra算法回答:。dijkstra算法是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。时间复杂度:朴素版写法O(V方) 进阶写法:基于优先队列的写法(适用于稀疏图)总复杂度为 O(V log E)Edge:边 Vertex:顶点10.谈一下你对STL的理解C++ STL 做为C++的一个标准类库,包含了复用性最高的数据结构(容器)与算法(模板函数)。STL的容器可以分为以下几个大类:一:序列容器,有vector, list, deque,string.二:关联容器可以分为 set(集合)和 map(映射表)两大类,及其衍生体 multiset 和 multimap。这些容器的底层机制均以 RB-tree(红黑树)实现。RB-tree 也是一个独立容器,但并不开放使用。SGI STL 还提供一个不在标准规格的关联式容器 hash_table(散列表),以及以 hash_table 为底层机制而完成的 hash_set散列集合、hash_map散列映射表、hash_multiset散列多键集合、hash_multimap散列多键映射表。关联容器,类似关联式数据库,每个元素都有一个键值key和一个实值value。关联式容器的内部结构是一个平衡二叉树,包括 AVL-tree、RB-tree、AA-tree,STl 中运用的是 RB-tree红黑树。11.STL的使用场景vector(可变长的动态数组)适用场景:需要快速查找,不需要频繁插入/删除的场景string:string 类型支持长度可变的字符串,实际上就是vector,便于程序员操作字符串的类库。也可以认为string是一个类:string封装了char* ,管理这个字符串,是一个char* 型的容器;array:数组 使用场景:类似vector,比数组更安全(不担心越界),但是内容在栈上,且属于定长容器。deque:(double-ended queue,双端队列)是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。使用场景:头尾增删元素很快,随机访问比vector慢一点,因为内部处理堆跳转。中间插入和删除效率交较高。但因为他是list和vector的结合体,出场次数不多。forward_list:使用场景:需要list的优势,但只要向前迭代list:需要频繁插入/删除,不需要快速查找queue:FIFO(先进先出)~( First Input First Output)。底层容器可以是list或deque。set/multiset:需要元素有序,查找/删除/插入性能一样。红黑树效率都是O(logN)。即使是几个亿的内容,最多也查几十次。map/multimap 映射/多重映射:需要key有序将值关联到key,O(logN)查找/删除/插入性能一样12.C++11语法熟悉吗回答:还行,之后问了std::function,我回答是std::function是一个函数包装模板,可以包装下列这几种可调用元素类型:函数、函数指针、类成员函数指针或任意类型的函数对象(例如定义了operator()操作并拥有函数闭包)。std::function对象可被拷贝和转移,并且可以使用指定的调用特征来直接调用目标元素。当std::function对象未包裹任何实际的可调用元素,调用该std::function对象将抛出std::bad_function_call异常。13.工程里面的回溯是什么?回答:用git一直备份版本有需要就回退一下14.Widget::Widget(QWidget *parent) :QWidget(parent)什么意思由于构造函数是指在创建一个新对象的时候,自动执行,因此通常用来实现一些默认操作。此处“Widget::Widget(QWidget *parent) ”定义派生类的构造函数;:QWidget(parent)基类的有参构造函数最终达到:调用基类的有参构造函数,实现对象树上基类的功能15.算法题:跳台阶(找规律可以发现是个斐波那契数列)一个楼梯共有 n 级台阶,每次可以走一级或者两级,问从第 0 级台阶走到第 n 级台阶一共有多少种方案。输入格式共一行,包含一个整数 n。输出格式共一行,包含一个整数,表示方案数。数据范围1≤n≤15输入样例:5输出样例:8#include<bits/stdc++.h>using namespace std;int a=1,b=2,c;int main(){    int n;    cin>>n;    int w=n-2;    if(n==1) cout<<1;    else if(n==2) cout<<2;    else    {        while(w--)        {            c=a+b;            a=b,b=c;        }        cout<<b;    }}123456789101112131415161718192016.算法题: 餐厅(区间贪心)一家餐厅收到了 n 个客人的预约订单。每个订单都有开始时间和结束时间。对于每个订单,餐厅有权利接单,也有权利拒单。接受的订单,两两之间不得有任何时间交集,甚至不得有时刻交集,即如果一个订单的开始时间和另一个订单的结束时间相同,则两订单也不得同时接受。为了赚更多钱,餐厅需要尽可能多的接单。请问,餐厅最多可以接多少单?输入格式第一行包含一个整数 n。接下来 n 行,每行包含两个整数 l,r,表示一个订单的开始时间和结束时间。输出格式输出可以接受的最大订单数量。数据范围1≤n≤5×105,1≤l≤r≤109输入样例1:27 114 7输出样例1:1输入样例2:51 22 33 44 55 6输出样例2:3输入样例3:64 81 54 72 51 36 8输出样例3:2#include<bits/stdc++.h>using namespace std;struct node{    int l,r;};int n;bool cmp(node x,node y){    return x.r<y.r;}int main(){    cin>>n;    node a[n+1];    int i;    for(i=0;i<n;i++)    {        cin>>a[i].l>>a[i].r;    }    sort(a,a+n,cmp);    int temp=a[0].r;    int tot=1;    for(i=1;i<n;i++)    {        if(a[i].l>temp)        {            temp=a[i].r;            tot++;        }    }    cout<<tot;}123456789101112131415161718192021222324252627282930313217.反问面试官环节自由发挥即可,可以提前准备一些大众问题和自己关心的问题。三、实习工作分享1.实习专有名词及分析篇实习中你肯定会接触到很多自己没不熟悉的名词,在这里算法小学徒给大家分享一些cpp后端专有名词的解释:1.1SDK:软件开发工具包(Software Development Kit)软件开发工具包一般都是一些软件工程师为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件时的开发工具的集合。SDK相当于是一个开发者集成的环境,作为APP供应链中重要的一环,在提升App兼容性和灵活性、节约开发成本方面表现卓著。一个产品想实现某些特定功能如消息推送,便可以找到相关的第三方SDK,工程师直接接入SDK,不用再重新开发。这样,工程师可以将更多的时间和精力投入到其他产品业务相关功能的开发上。1.2API:应用程序接口(Application Programming Interface)API又称为应用编程接口,就是软件系统不同组成部分衔接的约定。由于近年来软件的规模日益庞大,常常需要把复杂的系统划分成小的组成部分,编程接口的设计十分重要。程序设计的实践中,编程接口的设计首先要使软件系统的职责得到合理划分。良好的接口设计可以降低系统各部分的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合程度,从而提高系统的维护性和扩展性。简单地说:API就是接口,就是通道,负责一个程序和其他软件的沟通,本质是预先定义的函数。举例:windows串口通信函数API1.CreateFile - 打开串口;2.SetupComm-初始化一个指定的通信设备的通信参数3.ReadFile - 读数据;4.WriteFile - 写数据;5.CloseHandle - 关闭串口;6.GetCommState - 取得串口当前状态;7.SetCommState - 设置串口状态;8.PurgeComm - 清除串口缓冲区 ;9.ClearCommError - 清除串口错误或者读取串口现在的状态;10.SetCommMask - 设置串口通信事件;1.3上位机上位机是指可以直接发出操控命令的计算机。1.4下位机下位机是直接控制设备获取设备状况的计算机。1.5上位机与下位机区别1.上位机一般是集中管理监控机,下位机是指现场直接控制器或控制机。上位机面向管理级用户,下位机面向底层设备控制。2.上位机:上位监视系统,一般为计算机系统(监控软件;下位机:控制系统的现场执行系统,一般为PLC等设备。3上位机是指工业控制中位于较高层次的计算机,一般是指电脑;而下位机是直接控制设备获取设备状况的的计算机,一般是指PLC/单片机之类的。4.经验:通常工控机,工作站,触摸屏作为上位机;而通信控制PLC,单片机等作为下位机,从而控制相关设备元件和驱动装置。四、总结后面博主会继续为大家带来 C/C++后端实习经验大礼包的其他内容,希望大家喜欢,撒花~~,另外根据博主的亲身实习经验,自学能力不管是面试还是实习过程中都非常受青睐, 程序员行业是一个需要终身学习的行业,大部分时间别人教你的东西只能作为一个参考,而且自学这种能力可以让人感觉到了无限的可能和创造性,跨越认识边界,不受限制的感觉真的很奇妙每日一句:不是因为有希望才去努力,而是努力了,才能看到希望。成功重在努力与坚持,抓住时机,就趁现在!与大家共勉!!!————————————————版权声明:本文为CSDN博主「算法小学徒」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/wyhplus/article/details/125771094
  • [技术干货] C/C++ 指针小笔记-转载
    如果想要更详细的了解指针原理、用途,请自行阅读《The C Programming Language》第 5 章。指针是什么指针其实就是一个包含一个变量的地址的变量。如何定义指针定义指针的方式很简单:类型 *指针名称;即可。比如来声明一个变量名为a,类型为int的指针:int *a;1也就是比平时声明变量多了一个型号*。但是这里需要注意的是,声明时候的*a表示的是指针a包含的内存地址指向的内容,而a包含的则是地址。 这个地址开始的一部分连续的空间将会划分给指针,以供存储地址,划出的大小由类型int决定,一般int为 2 个字节(这里的一个字节在有些英文文档中被称为“cell”)。如何获取一个变量的地址刚才说到指针其实就是一个包含一个变量的地址的变量。那么如何获取一个变量的地址呢?这样才可以给指针赋值。方法很简单,使用&符号,如下(a就是上文声明的指针):a=&b;1通过以上表达式,就将变量b的地址赋值给指针a,如果这时候使用*a输出,就会发现输出的是b的值。而且修改b,输出*a的值也会发生改变。如果a并不是指针,那么变量a将会以十六进制存储变量b的地址(一般是十六进制,有些环境、设备可能会有不同)。下面展示输出一个变量地址的方法:#include <stdio.h>int main(){    int a=10;        printf("%x\n",&a);        return 0;}12345678910输出结果如下:bfeff2c8Program ended with exit code: 012可以看到,第一行的bfeff2c8就是变量a的地址。赋值给一个指针指针只负责存储地址,具体的值还是由变量存储。所以赋值给指针,就是将一个变量的地址赋值给它。如下:#include <stdio.h>int main(){    int a=10;    int *b;        b=&a;        printf("%d\n",*b);        return 0;}12345678910111213输出:10Program ended with exit code: 012指针的用途听了上述描述,可能你会觉得指针的作用好像就是新建了一个软连接(symbolic link)或者创建了一个别名(alias)。但是指针最大的作用其实是搭配数组使用。通过指针来使用数组因为一个数组的地址其实是数组第一个元素的地址,而数组在地址上是连续存放的,所以可以对地址加 1 来实现控制数组。如下(实际运行的时候可能会提醒,但是这里出于研究目的忽略提醒):#include <stdio.h>int main(){    int a[5]={1,2,3,4,5};    int *b;        b=&a;        printf("数组地址:%x\n",&a);    printf("数组第一个元素:%d\n",*b);    b=b+1;    printf("是不是数组第二个元素:%d\n",*b);        return 0;}12345678910111213141516输出:数组地址:bfeff2b0数组第一个元素:1是不是数组第二个元素:2Program ended with exit code: 01234这里说明一下,如果在开发过程中,指针指向数组的话,请使用b=&a[0];,这样不会出现提醒。字符串和指针字符串也是数组的一部分,所以也可以使用指针来实现一些功能,例如对比字符串(这个虽然不用指针也能写,但是使用指针的话,代码会变得更简洁)。下面来演示一下指针访问字符串的例子(记得把指针类型换一下,换成char,不然int存放的会是 ASCII编码,而不是字符串的内存地址):#include <stdio.h>int main(){    char a[20]="Hello! World!";    char *b;        b=&a[0];        printf("字符串的地址:%x\n",&a);    printf("字符串的第一个字符:%c\n",*b);    b=b+5;    printf("字符串的第六个字符:%c\n",*b);        return 0;}12345678910111213141516输出:字符串的地址:bfeff2b0字符串的第一个字符:H字符串的第二个字符:!Program ended with exit code: 01234其他用途还有一些其他的用途,但是有上述的知识也不用过多解释了,讲一下思路即可。更多维度的数组C 语言支持二维数组,但是有时候需要更多维度,那么就需要使用指针来嵌套数组,这样可以实现更多维度的数组。一般就是将指向多个数组的多个指针存放在一个数组中,然后通过上述方法来使用。处理命令选项这个一般使用不多,因为现在也没多少人使用命令行,都用图形化了,但是我还是要说一下。先看一下我的另一篇博客《C语言中函数main的参数argc和argv是什么》。argv是一个指向一个字符串数组的指针,数组包含了参数,每个字符串就是一个参数,最后一个元素为0。不过一般习惯使用多级指针来操作字符串。这里介绍一下,一般的命令行程序,使用起来的命令如下:程序名 -短选项 --长选项 内容1短选项为-加一个字母,长选项一般为--加一个单词。现在有些程序的短选项会省略-。短选项一般可以写到一起,举个例子:ls -a -f也可以写成ls -af。而这一功能的实现就需要使用指针(首先这是个存放了指向字符串指针的数组,其次指针真的更方便)。————————————————版权声明:本文为CSDN博主「zhonguncle」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_33919450/article/details/126129081
  • [技术干货] 、Python 比 C++ 慢 8 倍、29 倍?-转载
    整理 | 苏宓出品 | CSDN(ID:CSDNnews)近日,来自多伦多大学和 YScope 公司(为软件系统提供创新的日志管理和故障排除工具。由一群计算机工程教授和博士创立)的 David Lion、多伦多大学 Adrian Chiu 和 Michael Stumm、多伦多大学和 YScope 公司 Ding Yuan 共同发布了一份《调查托管语言的运行时性能:为什么 JavaScript 和 Python 比 C++ 慢了 8 倍和 29 倍,而 Java 和 Go 却能更快》(https://www.usenix.org/system/files/atc22-lion.pdf)的论文分析报告,深度剖析了不同编程语言运行时在代码开发中真实的性能情况,由此方便开发者可以精确地测量执行任何字节码指令所花费的时间等。性能是系统软件不得不面对的挑战在报告中,研究人员指出,自 2015 年以来,具有集成运行时环境的编程语言越来越受欢迎,其中,全球知名的代码托管平台 GitHub 上最受欢迎的三种语言分别是 JavaScript、Java 和 Python。作为开发利器,编程语言帮助开发者快速构建各种应用程序和服务,也极大地提高了生产力。同时,这些语言自身也提供了各种功能,如动态类型检查、带有垃圾收集的内存管理,以及动态内存安全检查等等。为此,研究人员用「托管语言」(managed languages)专业术语来指代这些类型的编程语言。现实来看,托管语言越来越多地被用于实现性能至关重要的系统软件上,如Hadoop 和 Spark 都在 Java 虚拟机(JVM)上运行,因为它们分别用 Java 和 Scala 实现;Kubernetes、etcd(分布式键值存储)和 M3(由 Uber 建立的分布式时间序列数据库和查询引擎)都是用 Go 实现的。当前,甚至连操作系统(OS)的内核 Biscuit 也是用 Go 实现的 。Openstack、Paypal、Instagram 和 Dropbox 都大量使用 Python,其中,Python 是 Dropbox "在后台服务和桌面客户端应用中使用最广泛的语言",在一个存储库中就有近 400 万行 Python 代码;JavaScript 也被用于 Facebook 的 Bladerunner pub/sub 系统的性能关键路径中。在开发过程中,编程语言的性能在一开始很少会被考虑到项目中,部分原因是不少开发者认为性能问题可以在以后慢慢去解决,也许可以通过简单地增加硬件来进行横向扩展。不过,随着代码产品或服务使用规模的扩大,服务变得越来越慢或者硬件成本变高,性能成为一个不容忽视的问题。这也是为什么 Stream 要放弃了 Python 而改用 Go、 Discord 从 Go 切换到 Rust、Twitter 从 Ruby on Rails 切换到 Scala 和 Java 的主要原因。不少开发者往往为了提升性能,想破脑袋,但现实只有两条路,一条是从现有的代码中想尽办法尽可能地做优化,另一条是思考使用的编程语言是否已经达到了性能极限,看看有没有必要将旧的代码移植到一个新的性能更高的语言上。为了彻底解开系统软件中不同编程语言导致的性能问题,研究人员决定以 C++ 为极限,对 Java、Go、JavaScript 和 Python 四种编程,还有应用最广泛的运行时系统 CPython、OpenJDK。Node.js 与 JavaScript 的 V8 引擎进行深入的定量性能分析。同时,研究人员还从头开始建立了 6 个应用程序,并创建了一个名为 LangBench 基准(https://github.com/topics/langbench)。这些应用程序涵盖了各种不同的计算强度、内存使用、网络和磁盘 I/O 强度以及可用的并发性的应用场景等复杂性。对此,研究人员全面分析了它们的完成时间、资源使用和可扩展性。测试方法值得一提的是,研究人员指出,这份论文没有也不可能全面地回答与语言运行时的性能有关的每一个问题。本文只是评估了四种语言的运行时,而且对于每种语言,只评估了最广泛使用的实现。此外,研究人员只在一个单一的操作系统/硬件堆栈上运行了工作负载。其研究结果与使用的基准有关,这些基准模拟了现实生活中的应用,但可能不代表广泛的应用。在测试方法上,研究人员在两台内部服务器上进行了实验,每台服务器有 2 个Xeon E5-2630V3、16 个虚拟核心、2.4GHz CPU、256GB DDR4 内存和两个 7200 RPM 硬盘。它们运行的系统是 Linux 4.15.0,并通过 10Gbps 的互联网络连接。对于 C++ 程序,研究人员使用的是 GCC 9.3.0 根据 C++17 标准用 -O3 进行编译。对于 OpenJDK 13、CPython 3.8.1 和Go 1.14.1 ,其使用了各自语言的参考实现。同时,使用 Node.js 13.12.0 和 V8 7.9.317.25 版本。研究人员对每个基准进行了 5 次测试,取平均值。其中,在运行键-值存储、日志分析器和文件服务器的基准时,client 和 worker 线程的数量从 1 到 1024 不等。对于 OpenJDK 和 V8 来说,最小的内存量是通过确定不会导致崩溃的第一个堆配置来设置的;对于 Go 来说,GOGC 被设置为5%。然后研究人员不断增加堆的设置,直到性能不再提高。其使用第一个设置的结果(即最小的堆大小)得出最佳性能。对于日志解析器和文件服务器基准,所用的日志文件被存储在一个复制系数为 2 的分布式文件系统上。 在运行每个基准之前,研究人员都清除了 Linux 的页面缓存,以保证测试准确性。其中,优化的 GCC 平均速度最快,Go 和 OpenJDK 紧随其后,比 GCC 慢了  1.30 倍和 1.43 倍。令人印象深刻的是,在 12 项基准测试中,Go 和 OpenJDK 有 3 项超过了优化的 GCC。总体而言,研究人员发现 V8 / Node.js 和 CPython 表现最差,执行应用程序的平均速度分别比 C++ 应用程序慢 8.01 倍和 29.50 倍,这意味着运行时下,JavaScript、Python 要比 C++ 慢这么多。更糟糕的是,这两个运行时上的应用程序扩展性很差,因为它们无法有效地利用多个内核。在极端情况下,CPython 比 GCC 慢了 129.66 倍(在排序基准中)。V8/Node.js 和 CPython 只有在工作负载受到磁盘 I/O 的瓶颈时,即在文件服务器基准中,才与 GCC 有竞争力。相比之下,OpenJDK 和 Go 应用程序即 Java 和 Go 语言比 C++ 更具有性能竞争力,运行速度仅慢了 1.43 倍和 1.30 倍,并且可以轻松扩展到多个内核。在一些应用中,OpenJDK 和 Go 的性能超过了 C++ 的同类产品。更多完整报告内容详见:https://www.usenix.org/system/files/atc22-lion.pdf
  • [分享交流] C++工程师视角下的Rust,有何不同?
    如果说C++在内存安全上做出了自己的努力,那么在线程并发安全上则努力程度还不够;而Rust则是从一开始就在内存安全和线程安全上下足了功夫,同时没有抛弃性能。在一些基本的语言表达方式上,Rust和C/C++存在一些不同,体现在:(1)变量默认是不可变绑定(let),需要修改变量,则需明确使用可变绑定(let mut);(2)没有实现Copy trait的对象,绑定、赋值、非引用传参时默认是移动语义;(3)支持函数内嵌定义;(4)支持函数表达式返回(最后不加分号);(5)在同一个作用域内,变量可以重新绑定(let),在Rust中叫做遮蔽机制;(6)支持零尺寸的结构体、空枚举、空数组([T, 0]);(7)两种字符串类型变量:&str相当于C++中的const char*,用于指向字符串字面常量;而String相对于C++中的std::string,支持可变引用&Mut String和不可变引用&String;(8)基本的数据类型都实现了Copy trait,默认在栈上分配,支持复制语义;而String、Vec等默认只支持移动语义,要进行深拷贝,需要显式调用clone函数;(9)不支持switch & case,使用match模式匹配代替;(10)不支持三目运算符;(11)支持?运算符,用于调用的函数返回异常时,直接退出当前函数并返回对应的错误Err<T>;……
  • [技术干货] C++常见面试题总结[转载]
    1、C和C++的区别1)C是面向过程的语言,是一个结构化的语言,考虑如何通过一个过程对输入进行处理得到输出;C++是面向对象的语言,主要特征是“封装、继承和多态”。封装隐藏了实现细节,使得代码模块化;派生类可以继承父类的数据和方法,扩展了已经存在的模块,实现了代码重用;多态则是“一个接口,多种实现”,通过派生类重写父类的虚函数,实现了接口的重用。2)C和C++动态管理内存的方法不一样,C是使用malloc/free,而C++除此之外还有new/delete关键字。3)C++中有引用,C中不存在引用的概念2、C++中指针和引用的区别1)指针是一个新的变量,存储了另一个变量的地址,我们可以通过访问这个地址来修改另一个变量;引用只是一个别名,还是变量本身,对引用的任何操作就是对变量本身进行操作,以达到修改变量的目的2)引用只有一级,而指针可以有多级3)指针传参的时候,还是值传递,指针本身的值不可以修改,需要通过解引用才能对指向的对象进行操作引用传参的时候,传进来的就是变量本身,因此变量可以被修改3、结构体struct和共同体union(联合)的区别结构体:将不同类型的数据组合成一个整体,是自定义类型共同体:不同类型的几个变量共同占用一段内存1)结构体中的每个成员都有自己独立的地址,它们是同时存在的;共同体中的所有成员占用同一段内存,它们不能同时存在;2)sizeof(struct)是内存对齐后所有成员长度的总和,sizeof(union)是内存对齐后最长数据成员的长度、结构体为什么要内存对齐呢?1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常2.硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。4、#define和const的区别1)#define定义的常量没有类型,所给出的是一个立即数;const定义的常量有类型名字,存放在静态区域2)处理阶段不同,#define定义的宏变量在预处理时进行替换,可能有多个拷贝,const所定义的变量在编译时确定其值,只有一个拷贝。3)#define定义的常量是不可以用指针去指向,const定义的常量可以用指针去指向该常量的地址4)#define可以定义简单的函数,const不可以定义函数5、重载overload,覆盖(重写)override,隐藏(重定义)overwrite,这三者之间的区别1)overload,将语义相近的几个函数用同一个名字表示,但是参数列表(参数的类型,个数,顺序不同)不同,这就是函数重载,返回值类型可以不同特征:相同范围(同一个类中)、函数名字相同、参数不同、virtual关键字可有可无2)override,派生类覆盖基类的虚函数,实现接口的重用,返回值类型必须相同特征:不同范围(基类和派生类)、函数名字相同、参数相同、基类中必须有virtual关键字(必须是虚函数)3)overwrite,派生类屏蔽了其同名的基类函数,返回值类型可以不同特征:不同范围(基类和派生类)、函数名字相同、参数不同或者参数相同且无virtual关键字6、new、delete、malloc、free之间的关系new/delete,malloc/free都是动态分配内存的方式1)malloc对开辟的空间大小严格指定,而new只需要对象名2)new为对象分配空间时,调用对象的构造函数,delete调用对象的析构函数既然有了malloc/free,C++中为什么还需要new/delete呢?运算符是语言自身的特性,有固定的语义,编译器知道意味着什么,由编译器解释语义,生成相应的代码。库函数是依赖于库的,一定程度上独立于语言的。编译器不关心库函数的作用,只保证编译,调用函数参数和返回值符合语法,生成call函数的代码。malloc/free是库函数,new/delete是C++运算符。对于非内部数据类型而言,光用malloc/free无法满足动态对象都要求。new/delete是运算符,编译器保证调用构造和析构函数对对象进行初始化/析构。但是库函数malloc/free是库函数,不会执行构造/析构。7、delete和delete[]的区别delete只会调用一次析构函数,而delete[]会调用每个成员的析构函数用new分配的内存用delete释放,用new[]分配的内存用delete[]释放一.构造函数构造函数是和类名相同的一个函数,它的作用是实现对象的初始化。当对象被创建时,构造函数自动被调用。特点:没有类型没有返回值(也不用写void)名字与类名相同可重载!作用:完成类的对象的初始化Cdate d; //定义对象d注意:当对象d被创建时,会自动调用构造函数 d.Cdate()。当类中未定义构造函数时,编译器会自动假设存在以下两个默认构造函数:(此构造函数什么都不做,就是个形式)。如果作者自己定义了构造函数,则默认的构造函数不会存在。//默认构造函数一 Cdate::Cdate() { } //默认构造函数二 Cdate::Cdate(const Cdate& a) { }三.析构函数我们已经知道构造函数是在创建对象时,对其进行初始化。而析构函数与其相反,是在对象被删除前象由系统自动执行它做清理工作。作为一个类,可能有多个对象,每个对象生命结束时都要调用析构函数,且每个对象调用一次。特点:无类型无返回值名字与类名相同不带参数,不可重载,析构函数只有一个!析构函数前“~” (取反符,表示逆构造函数)作用:在对象被删除前做清理工作。注意:对象的析构函数在对象被销毁前被调用,对象何时销毁也与其作用域相关。例如,全局对象是在程序运行结束时销毁;自动对象是在离开其作用域时销毁;而动态对象是在使用delete运算符时销毁。析构函数特别适用于当一个对象被动态分配内存空间,而在对象被销毁前希望释放它所占用的内存空间的时候。我们不会忽略初始化的重要性,却常常忽略清除的重要性,然而对销毁变量的内存清理是非常重要的。例如,我们在堆中申请了一些内存,如果没有用完就释放,会造成内存泄露,会导致应用程序运行效率降低,甚至崩溃,不可掉以轻心。而在c++中提供有析构函数,可以保证对象清除工作自动执行。析构与构造的调用次序相反,即最先构造的最后被析构,最后构造的最先被析构。7.1、虚函数、纯虚函数虚函数:虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数,是C++中多态性的一个重要体现。利用基类指针访问派生类中的虚函数,这种情况下采用的是动态绑定技术。纯虚函数:纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”.纯虚函数不能实例化对象。抽象类的介绍抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。(1)抽象类的定义: 称带有纯虚函数的类为抽象类。(2)抽象类的作用: 抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。(3)使用抽象类时注意:抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。抽象类是不能定义对象的。总结:1、纯虚函数声明如下: virtual void funtion1()=0; 纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用。2、虚函数声明如下:virtual ReturnType FunctionName(Parameter) 虚函数必须实现,如果不实现,编译器将报错,错误提示为:3、对于虚函数来说,父类和子类都有各自的版本。由多态方式调用的时候动态绑定。4、实现了纯虚函数的子类,该纯虚函数在子类中就编程了虚函数,子类的子类即孙子类可以覆盖该虚函数,由多态方式调用的时候动态绑定。5、虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。6、在有动态分配堆上内存的时候,析构函数必须是虚函数,但没有必要是纯虚的。纯虚函数的引入,是出于两个目的:1、为了安全,因为避免任何需要明确但是因为不小心而导致的未知的结果,提醒子类去做应做的实现。2、为了效率,不是程序执行的效率,而是为了编码的效率。动态绑定:基类指针是调用派生类的中的成员函数还是调用基类中的成员函数要到程序运行时确定。主要看此时基类指针所指向的对象。 这里要涉及一些很重要的概念,也是我最近看完Effective C++才明白的东西,记录下来。这些概念就是静态类型和动态类型,静态绑定和动态绑定。静态绑定和动态绑定。静态绑定是说前期绑定。 所谓对象的静态类型,就是它在程序中被声明的时候采用的类型。 考虑下面的class继承体系:class Shape{ virtual void draw(color = Red) const=0; ... ... }; class Rectangle:public Shape{ virtual void draw(color = Red) const; ... ... }; class Circle:public Shape { virtual void draw(color = Red) const;   ... ... }; 现在考虑以下这些指针: Shape* ps;//静态类型为Shape* Shape*pc =new Circle;//静态类型Shape* Shape*pr = new Rectangle;//静态类型Shape*在本例中,ps,pc,pr都被声明为Shape*类型的,所以它们的静态类型都是Shape*。注意:无论它们真正指向什么,它们的静态类型都是Shape*. 所谓的对象的动态类型是指“当前所指对象的类型”。也就是说,动态类型可以表现出一个对象将会有什么行为。根据上面的例子,pc的动态类型是Circle*,pr的动态类型是Rectangle*。ps没有动态类型,因为它没有指向任何对象。 动态类型一如其名所示,可以在执行过程中改变(通常是经过赋值运算):ps=pc; \\ps的动态类型如今是Circle* ps=pr; \\ps的动态类型如今是Rectangle*Virtual函数系动态绑定而来,意思是调用一个virtual函数的时候,究竟调用的是哪一个函数代码,取决于发出调用的那个对象的动态类型。ps->draw(); \\调用的是Rectangle::draw(Red)8、STL库用过吗?常见的STL容器有哪些?算法用过几个?STL包括两部分内容:容器和算法容器即存放数据的地方,比如array, vector,分为两类,序列式容器和关联式容器序列式容器,其中的元素不一定有序,但是都可以被排序,比如vector,list,queue,stack,heap, priority-queue, slist关联式容器,内部结构是一个平衡二叉树,每个元素都有一个键值和一个实值,比如map, set, hashtable, hash_set算法有排序,复制等,以及各个容器特定的算法迭代器是STL的精髓,迭代器提供了一种方法,使得它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构,它将容器和算法分开,让二者独立设计。Vector是顺序容器,是一个动态数组,支持随机存取、插入、删除、查找等操作,在内存中是一块连续的空间。在原有空间不够情况下自动分配空间,增加为原来的两倍。vector随机存取效率高,但是在vector插入元素,需要移动的数目多,效率低下。注意:vector动态增加大小时,并不是在原空间之后持续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。————————————————版权声明:本文为CSDN博主「Cpp编程小茶馆」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/xu_fu_yong/article/details/122948379
  • [应用开发] 【MDC300】【AI推理】在部署时是选python还是c++
    【功能模块】深度学习模型部署【操作步骤&问题现象】1、深度学习模型(如检测、跟踪)部署时,是选用c++还是培养python?mdc300 mini上能否pip安装?python是否支持和c++程序之间的通信?2、modelzoo给的模型大部分是基于python的,是否能支持转换为om模型?算子是否支持?
  • [技术干货] C/C++在嵌入式中地位不保,Rust将成为更好的“备胎”?【转载】
    【CSDN 编者按】在嵌入式环境中,C、C++作为最常见的编程语言,早已被广泛应用在底层工具链、库中。不过,近日嵌入式工程师Omar Hiari提出一种完全不同的看法,他认为一开始就考虑安全问题的编程语言Rust才是嵌入式领域的未来。虽然要将嵌入式应用程序代码迁移到一种新的编程语言上非常麻烦,但他认为这是可行的,只是需要一些方法和实践。软件故障在嵌入式领域时常发生,随着物联网(IoT)和网络物理系统(CPS)等技术的普及,这些故障也没有得到应有的重视。当人们谈论自动驾驶汽车、机器人、无人机时,他们总认为这些出错的机率很小。但在了解一些历史上著名的软件事故、Bug案例研究后,你就不会这么想了。重现问题简直是一场噩梦首先,需要声明的是,我主要从事汽车嵌入式领域。此外,在职业生涯中,我还接触到了功能安全领域。功能安全,简单来说,是指一个系统或是设备整体安全的组成部分。其达成安全性的方式是靠系统或组成零件在接受输入讯号后,可以正常的动作,来减少导致人类受伤的事故风险。例如一个马达中加装温度感测器,若温度超过一定值,即停止马达运转,此机能就属于功能安全。在进入到汽车行业这些年,我担任过几个项目的技术负责人,其中部分职责会涉及到处理客户退货的问题。之所以这些汽车会被退回,大概率是由于电子单元或模块出现了问题,而我们团队需要确定导致该问题的根本原因。如果问题的根源确认是模块中出现了一个错误,我们会针对这个错误提出一个修复方案,并且将它整合到未来的更新中。有时我们会花费几天,甚至是几周来找出Bug的原因。我现在还记得我第一次处理这些问题的的经历,那时我还是一个做模块测试的实习生。我们发现,有几个被退回的模块,在现场测试时出现了不一样的结果。有些能够正常工作,有些不能。在和软件团队调试了很长时间后,我们终于找出了问题所在,原来是因为一个未初始化的变量!我惊讶于这样的错误竟然没有被检测出来。随着在这个行业不断地成长,我所遇到的问题越来越复杂。有时,我们会在成千上万的模块中找一个有问题的模块。通常情况下,第一步是尝试复现这种问题,以便用调试工具追踪问题的来源。这简直是一场噩梦!复现某些行为需要几天或者几周的时间,有时甚至需要更长的时间,因为要复现这个问题,需要非常具体的实现过程相组合。在大多数情况下,这些实现过程会存在一些未经测试的配置,最终导致触发Bug。正常导致汽车故障的大多数问题本质上出在软件上。有趣的是,即使是在一些质量至上,而且拥有大量经验丰富软件工程师的公司也会出现这种情况。基本上,这些遇到现场问题的模块,已经进行了彻底的测试、代码审查、代码标准的实施(例如MISRA-C)。一开始就考虑安全问题?后来,在从汽车嵌入式领域进入功能安全领域时,我发现我的经历更加有趣。在功能安全方面,像工业IEC-61508和汽车ISO-26262这样的标准在汽车行业开始流行起来。遵循这些标准的目的是为了减少产品故障的风险,风险的大小取决于产品的使用方式或者地点。通过在开发过程中加入更多的测试和检查,可以让产品达到一定的安全水平。以ISO-26262为例,该标准对软件和硬件中可能发生的故障类型进行了分类。硬件的故障被分为两种类型:随机硬件故障和系统故障。在软件方面,只有一种类型,即系统性故障。根据ISO-26262,随机硬件故障被定义为:在硬件元素的生命周期中,可能会发生不可预测的故障,并且遵循概率分布。该标准还补充说:随机硬件故障率可以以合理的精度进行预测。而系统性故障被定义为:故障以确定的方式与某种原因相关,只能通过改变设计或制造过程、操作程序、文件或其他相关因素来消除。这意味着,系统故障或多或少源于人为错误,并不是真正可以预测的。因此,如果我们考虑修复软件问题,就意味着需要更多额外的检查。当然,在应用程序中也可以添加一些方法让其自己检查(例如N-版本编程),尽管如果把这些方法添加到动态的应用软件代码中,消耗的内存空间会增多。鉴于我前面提到的,人们可能会认为这种变化是喜闻乐见的。但情况恰恰相反,大多数工程师都会反对整合功能安全流程。事实上,如何让工程师和团队接受这一点,让许多管理安全开发者感到很头疼。这种推动力通常来自于产业链的顶端,即建立一种 “安全文化”。我认为,部分原因是由于流程和额外的文件或者可交付成果方面的重大变化,而不一定是因为个人反对安全的想法本身。(如果有什么是是工程师不喜欢做的,我想写文件一定排在首位。)还有一点也让管理安全开发者感到头疼,即说服团队,让他们知道产品需要从一开始就以安全前提来设计。这意味着,工程师不应该只致力于让现有产品开始应用其功能来满足安全要求。换句话说,就是对现有产品进行修补以使其满足安全要求。这主要是针对硬件和应用软件来说。让我感到不解的是,为什么这一项不能应用在编译语言或者编译器上呢?与编译器一起使用的语言并没有从一开始就考虑到安全问题。相反,它们是根据功能安全应用中的标准准则进行修改的。比如创建语言的”子集“,删除其中被认为不安全的部分。这意味着排除了编程语言结构中存在的不安全因素。迫在眉睫的问题对于一些了解甚少的人来说,在汽车领域最普遍的编程语言是C。但从我的亲身经历来看,我认为C、C++并不是能够引领未来应用趋势的语言。事实上,从长远来看,坚持使用C、C++,不管有多少标准引入都令我感到担忧。毫无疑问,C、C++是强大的语言,但与即将到来的应用程序相比,目前的应用程序还是太简单了。此外,随着行业的发展,工程师的水平会参差不齐。所以无论流程多么严格,出现系统性错误的可能性都会越来越大。随着经验的不断积累,实践不断增加,以及在C、C++这样的语言中不断增加补丁,但是类似的问题仍然是由于系统性错误而产生,是不是至少应该考虑切换到另一种思路来设计语言,即一开始就把安全性作为前提来设计?又或者只是创造出一种更加现代的语言,正如系统性故障所定义的一样:只能通过改变设计来消除问题。一条可能的前进道路我在Rust中找到了一条可能通往嵌入式的道路,它似乎就是我在这个行业多年来所要找的。Rust的设计初衷是为了成为一种安全的语言。当然,要把嵌入式程序代码迁移到一种新的编程语言上是非常麻烦的。显然,阻碍因素之一是在汽车等嵌入式环境中,C、C++的工具链和库已经根深蒂固了,早已成为了生态系统中的一部分。然而,虽然代码迁移有困难,但也不是不可能,我们可以制定计划,循环渐进地进行切换。在非嵌入式环境中,Rust已经获得了相当大的知名度,并且得到了亚马逊、Discord、Dropbox、Facebook、谷歌和微软等公司的投资。事实上,微软和谷歌已经为Rust在某些领域能够消除70%的安全问题做了担保。到目前为止,关于嵌入式,某些团体已经有了一些有趣的动向。有一个Rust嵌入式工作组正在社区内工作,以弥合与Rust团队的差距,同时发展嵌入式生态系统。该小组在发展生态系统方面的速度令人印象深刻。(如果对这个工作小组的成就感兴趣可以访问这个网站:https://www.autosar.org/news-events/details/autosar-announces-new-working-group-for-programming-language-rust-in-automotive-software-context-202/)另一个是Ferrous Systems,它在支持Rust生态系统方面也做了大量工作。Ferrous在为Rust创建不同的工具和扩展方面做出了重大努力,并且正在ferrocene项目下为ISO26262认证Rust编译器工具链。有趣的是,在我写这篇文章的时候,AUTOSAR(AUTomotive Open System ARchitecture汽车开放系统架构)也宣布了一个在新的汽车背景下的Rust工作小组。(感兴趣可以访问这个链接:https://www.autosar.org/news-events/details/autosar-announces-new-working-group-for-programming-language-rust-in-automotive-software-context-202/)(补充:我不属于也没有参与过上述任何实体)Rust可能是嵌入式未来市场方向表明,我们开始从C、C++向更安全、更现代的编译型编程语言转变的时机到了。(并不是对于所有的应用,只是对于那些C或者C++可能出现问题的应用)Rust编程语言虽然相当年轻,但似乎是最适合这种情况的。对于公司和个人来说,使用Rust编程可能是一个很好的战略决定,能够在未来获得优势。对于个人来说,即使像Rust这样的语言永远不会被采用,但它至少会给个人一个全新的视角,让他知道如何成为一个更好的C/C++开发者。原文链接:https://apollolabsblog.hashnode.dev/why-you-should-be-worried-about-the-future-of-cc-in-embedded-a-case-for-rust