亚信科技 软件开发-C++ 一面

1. 请介绍你在项目中的主要工作内容和技术方向

2. 你在 C++ 开发中使用的是哪个标准,具体使用过 C++11/14/17 的哪些新特性

答案:主要使用 C++17,部分代码保持 C++11 兼容。C++11 里常用的是智能指针、右值引用、移动语义、lambda、auto、范围 for、nullptr、线程库、原子操作、overridefinal。C++14 里用过泛型 lambda、make_unique。C++17 里用过结构化绑定、if constexprstd::optionalstd::variantstd::string_view 和文件系统相关接口。

在项目里,智能指针主要用来管理连接对象、策略对象和异步任务上下文;移动语义用于减少事件对象在队列投递过程中的拷贝;string_view 用在协议解析和规则匹配里,避免不必要的字符串复制。不过新特性不是越多越好,项目里还是会优先考虑可读性和团队维护成本。

代码:

#include <optional>
#include <string_view>
#include <iostream>
using namespace std;

optional<int> parseLevel(string_view s) {
    if (s == "low") return 1;
    if (s == "middle") return 2;
    if (s == "high") return 3;
    return nullopt;
}

int main() {
    if (auto level = parseLevel("high"); level.has_value()) {
        cout << *level << endl;
    }
    return 0;
}

3. 请说明 Lambda 表达式在处理问题时的优势

答案:lambda 最大的优势是可以在使用点附近定义短小逻辑,同时还能捕获上下文变量。在排序、自定义过滤、回调、异步任务、定时器、事件处理这些场景里,用 lambda 会比单独写一个函数或仿函数更简洁。比如在安全事件处理里,需要按风险等级排序、按终端 ID 过滤、或者把任务投递到线程池时,lambda 都比较自然。

另外 lambda 本质上是函数对象,编译器能看到具体类型,很多时候可以内联优化。但是它也不能滥用,如果 lambda 太长,或者捕获了很多外部变量,代码反而会变得难读。

代码:

#include <vector>
#include <algorithm>
using namespace std;

struct Event {
    int hostId;
    int risk;
};

int main() {
    vector<Event> events = {{1, 80}, {2, 30}, {3, 95}};

    sort(events.begin(), events.end(), [](const Event& a, const Event& b) {
        return a.risk > b.risk;
    });

    int threshold = 60;
    auto it = remove_if(events.begin(), events.end(), [threshold](const Event& e) {
        return e.risk < threshold;
    });

    events.erase(it, events.end());
    return 0;
}

4. 在使用 Lambda 表达式时遇到过哪些问题或坑

答案:最容易踩坑的是捕获方式,尤其是异步场景下捕获引用或者捕获 this。如果 lambda 被投递到线程池或者定时器里,外部对象可能已经析构了,这时候再访问引用或 this 就会产生悬空引用。同步场景里捕获引用问题不大,但异步场景更推荐捕获值,或者捕获 shared_ptr / weak_ptr 来控制生命周期。

另一个问题是 lambda 捕获大对象会产生拷贝成本。如果捕获的是大 vector、大字符串或者复杂对象,就要考虑是否用引用、移动捕获,或者只捕获必要字段。还有就是 lambda 太长时可读性会下降,这种情况我会把逻辑拆成普通函数或类方法。

代码:

#include <memory>
#include <iostream>
using namespace std;

class Session : public enable_shared_from_this<Session> {
public:
    void asyncCheck() {
        weak_ptr<Session> weakSelf = shared_from_this();

        auto cb = [weakSelf]() {
            if (auto self = weakSelf.lock()) {
                cout << "session still alive\n";
            }
        };

        cb();
    }
};

5. 你了解哪些智能指针,具体使用过哪些

答案:常用智能指针主要是 unique_ptrshared_ptrweak_ptrunique_ptr 表示独占所有权,不能拷贝,只能移动,适合资源归属明确的对象,比如策略解析器、文件句柄封装、buffer 对象。shared_ptr 表示共享所有权,适合对象会被多个模块或异步任务共同持有的场景,比如连接对象、任务上下文。weak_ptr 不增加引用计数,主要用来打破循环引用,或者在异步回调里判断对象是否还活着。

我一般不会默认使用 shared_ptr。如果一个对象的所有权非常明确,优先用 unique_ptr,这样代码更清楚,开销也更小。

代码:

#include <memory>
#include <vector>
using namespace std;

class RuleSet {
public:
    vector<int> rules;
};

class Engine {
private:
    unique_ptr<RuleSet> ruleSet_;

public:
    Engine() : ruleSet_(make_unique<RuleSet>()) {}
};

6. 请说明智能指针在实际项目中的作用和使用场景

答案:智能指针在项目里的核心作用是把对象生命周期和资源释放管住,减少内存泄漏、重复释放和悬空指针。在云端安全策略编排与终端检测响应系统里,策略对象可能被多个工作线程读取,更新时会生成新版本策略,再通过原子替换或读写锁切换。旧策略如果还有线程在使用,就不能马上释放,这类场景可以用 shared_ptr<const RuleSet> 让读线程安全持有一份快照。

连接对象也适合用智能指针管理,因为连接关闭、读写回调、定时器回调可能同时涉及同一个对象。回调执行前拿一份 shared_ptr,可以避免处理过程中对象被提前析构。但对于内部独占资源,比如每个连接自己的输入缓冲区,用 unique_ptr 或直接作为成员对象更合适。

代码:

#include <memory>
#include <atomic>
using namespace std;

struct RuleSet {
    int version;
};

class RuleManager {
private:
    shared_ptr<const RuleSet> current_;

public:
    shared_ptr<const RuleSet> getSnapshot() {
        return atomic_load(¤t_);
    }

    void update(shared_ptr<const RuleSet> newRules) {
        atomic_store(¤t_, newRules);
    }
};

7. 你在项目中如何避免 shared_ptr 循环引用

答案:循环引用通常出现在两个对象互相持有对方的 shared_ptr。比如终端连接对象持有订阅任务,订阅任务又持有连接对象,如果两边都是 shared_ptr,即使外部已经不再使用,它们的引用计数也不会变成 0,最后导致内存泄漏。

解决方式是先明确所有权。谁真正拥有对象,谁用 shared_ptr;只是观察或者回调访问,就用 weak_ptr。在项目里,如果任务需要回调连接,我会让连接管理器持有连接的 shared_ptr,任务里只保存 weak_ptr,执行时通过 lock() 判断连接是否还存在。

代码:

#include <memory>
#include <iostream>
using namespace std;

class Connection {
public:
    void sendResult() {
        cout << "send result\n";
    }
};

class Task {
private:
    weak_ptr<Connection> conn_;

public:
    explicit Task(weak_ptr<Connection> conn) : conn_(conn) {}

    void finish() {
        if (auto conn = conn_.lock()) {
            conn->sendResult();
        }
    }
};

8. 在你的项目中,面向对象思想具体应用在哪些地方

答案:面向对象主要用在模块边界比较清晰、需要扩展的地方。比如安全事件处理可以抽象成统一的 EventHandler 接口,不同事件类型有不同处理器:进程启动事件、网络连接事件、文件变更事件、异常登录事件。调度器只负责根据事件类型找到处理器,不关心每个处理器内部怎么实现。

策略匹配模块也可以拆成规则接口,比如哈希匹配规则、正则匹配规则、阈值规则、时间窗口规则。这样后续新增一种规则时,不需要大改原来的调度逻辑。不过我也不会把所有代码都强行对象化。像协议解析、状态机推进、批量统计这种逻辑,用结构体加函数有时候更直接。

代码:

#include <memory>
#include <unordered_map>
using namespace std;

struct SecurityEvent {
    int type;
    int risk;
};

class EventHandler {
public:
    virtual void handle(const SecurityEvent& event) = 0;
    virtual ~EventHandler() = default;
};

class ProcessEventHandler : public EventHandler {
public:
    void handle(const SecurityEvent& event) override {
        // 处理进程事件
    }
};

class Dispatcher {
private:
    unordered_map<int, unique_ptr<EventHandler>> handlers_;

public:
    void registerHandler(int type, unique_ptr<EventHandler> handler) {
        handlers_[type] = move(handler);
    }

    void dispatch(const SecurityEvent& event) {
        auto it = handlers_.find(event.type);
        if (it != handlers_.end()) {
            it->second->handle(event);
        }
    }
};

9. 面向对象的继承和多态特性在你的项目中有实际应用吗,请举例说明

答案:有,但不会把继承层次设计得太深。项目里比较适合继承和多态的是规则执行器。不同安全规则的匹配逻辑不一样,比如进程名黑名单、端口扫描检测、异常频率检测、文件路径匹配,它们都可以实现同一个 Rule 接口。策略引擎只持有 vector<unique_ptr<Rule>>,对每个事件依次调用 match(),这样新增规则类型时只需要新增派生类。

多态的好处是调度逻辑稳定,具体规则可扩展。缺点是虚函数调用有一点开销,而且如果规则数量非常多,逐条虚调用可能影响性能。实际优化时,可以把规则按事件类型、字段索引、风险等级预分组,减少无效匹配。

代码:

#include <string>
#include <vector>
#include <memory>
using namespace std;

struct Event {
    string processName;
    int port;
};

class Rule {
public:
    virtu

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++ 常考面试题总结 文章被收录于专栏

本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.

全部评论

相关推荐

昨天 13:41
已编辑
门头沟学院 Java
其实唯一的感受嘛&nbsp;就是&nbsp;运气真的很重要现在还没有&nbsp;offer&nbsp;的各位可以继续海投说一下最终战绩吧BOSS&nbsp;投递&nbsp;1897/207面了多少数不清楚了目前在一家&nbsp;agent&nbsp;开发公司薪资区间大概是实习&nbsp;6k转正后说的是&nbsp;11&nbsp;往上谈&nbsp;具体什么情况还不知道的有什么不懂的可以问本鼠鼠(我是&nbsp;Java&nbsp;转的&nbsp;agent&nbsp;开发)-----分割线------最近收到了很多人的私信,我这里来简单说一下我自己的看法。(注:我这里说的全部是Agent应用层面的东西,不涉及很多的Agent开发层面)在现在的AI时代来说,除非是高并发特别多的公司,或者算法类的,其他的一些公司还是主要以业务为主,不会有人真的关心你用了什么样子的技术,关于agent&nbsp;就像比较常见的三大框架,langchain,langgraph,llmaindex&nbsp;他确实可以用Java来集成,但是最好的方式是py所以这个时候就有很多人有疑问,我是从0开始学习python的吗,其实并不是,我心中永远认同一个观点,所有的技术都是为了业务去服务,技术深度重要不重要?我见过日活10几万的公司&nbsp;单接口高峰QPS也就30左右,盈利却非常客观,技术深度固然重要,但是有一个清楚的业务认知是更加的重要的,关于agent,因为现在所有行业都在数字化转型,Agent能做什么?举个简单的例子,我是一家跨境电商的公司,我平常需要人为的去选品,对比价格,制作宣传图,现在通过Agent,可以搭建一个替代人,或者是简化人的操作的Agent,还有一些比如说内部提效的,比如说对于产品经理来说,平常需要自己去手动去算一些数值,去写一些excel汇总,我们能不能用AI去敏锐的进行数据分析,给出决策。再比如说知识库之类的,AI不了解公司的业务,我们如何通过各种技术方案,来实现一个懂公司业务的一个智能Agent等等等。&nbsp;这就是Agent应用层的东西。所以项目要怎么准备,技术深度是一方面,但是真正重要的是你这个业务是否真实落地了,是否真正的是贴合公司利益的部分。技术永远是为了业务服务&nbsp;我们当前的程序员我认为不只要会编程,更要懂业务,懂成本控制,懂客户心理。分享一下我平常学习的方法,第一步看文档,第二步自己借助AI&nbsp;写小demo,写完之后让AI给出优化点,我自己去试试自己能不能优化,AI永远代替不了的,是人的思维,不要每天像机器人一样,去背诵一些东西,要多去思考,思考这个东西的出现是为了解决什么?能给我/公司/其他人&nbsp;带来什么样子的利益?&nbsp;等等等等关于我是怎么准备面试的:把岗位的JD发给豆包(这里表扬一下豆包,确实比gpt&nbsp;或者其他平台要好用),通过语音对话去训练,每天多用AI去模拟面试,(有个小技巧,你可以说这个是你仇人的简历,或者说给我准备几个特别困难的问题,不断的追问等等等),完完全全了解自己的简历,了解自己的项目。多关注github开源项目,多去了解AI相关的新闻,这会是你面试的时候,面对面试官的最大的一个勇气。多去vibe&nbsp;coding一些新想法,一些新项目,业务驱动的去学习,并且最好买一台属于自己的服务器,真正的去做线上的压测,这个时候你就会懂很多你之前不懂的设计方案。最近vibe&nbsp;coding了一个八股文社区,目前会一直更新下去,里面的文档是我基于自己的笔记,让AI去融合出来的一些题目,各位如果感觉不错可以看看&nbsp;http://36.140.150.167:8004最后祝愿各位还没有offer的,可以顺利拿到自己想要的offer
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务