三个比较小的C++项目与简历内容(json,跳表,线程池)

对于可以写到简历上的项目,webserver已经烂大街了,所以这里放出三个百行的小项目——(json解析器,跳表,线程池)。

当然,这三个东西就是用来充数或者是"八股触发器"。所以对于有比较好的项目人来说,是比较简陋的。

当然为了当好八股触发器的作用,我会给出每个小项目可以写在简历上的东西,并且给出相应的八股内容。

(所以在阅读的时候,你可以先阅读八股内容来进行一波学习)

json解析器

内容

项目名称:使用C++17标准的json解析器

使用variant 管理json数据类型 ,string_view进行代理模式明确只读语义,optional处理非侵入式异常处理机制。

通过递归下降对json字符串进行parser,解析后支持动态修改,并重新输出json格式,接口设计便捷,使用方便。

使用

int main() {
    std::ifstream fin("json.txt");
    std::stringstream ss; ss << fin.rdbuf();
    std::string s{ ss.str() };
    auto x = parser(s).value();
    std::cout << x << "\n";
    x["configurations"].push({true});
    x["configurations"].push({Null {}});
    x["version"] = { 114514LL };
    std::cout << x << "\n";
}

八股内容

使用variant 管理json数据类型

json的数据类型有很多,我们需要维护一个type来记录当前的类型,但是我们懒,不想手动维护怎么办呢?

严格鸽:C/C++ union 使用教程 (常见操作与缺陷)

严格鸽:现代C++学习——实现多类型存储std::variant

string_view进行代理模式明确只读语义

如果我们想要强调一个东西,他是只读的,可以选择用const来修饰他。

但是C++还提供了const_cast 这种东西,所以就有了view来明确只读的语义。

讨论这个的时候还可以讨论一下所有权的概念

C++ 17 std::string_view使用介绍

严格鸽:现代C++学习 —— 为什么需要std::move

严格鸽:现代C++学习—— 什么是RAII

optional处理非侵入式异常处理机制

如果json格式串是非法的怎么办?抛出一个异常,然后catch?

当然可以,但是C++17给了我们另外的做法

茗一:C++17 新特性之 std::optional(上)

在C++23中给出了更好的东西

std::expected - cppreference.com

通过递归下降对json字符串进行parser

递归下降是常见的parser方式

严格鸽:自己写一个编程语言(3) 从json入门的语法解析

代码

#include <iostream>
#include<variant>
#include<vector>
#include<map>
#include<optional>
#include<string>
#include<fstream>
#include <sstream>
namespace json {
    
    struct Node;
    using Null = std::monostate;
    using Bool = bool;
    using Int = int64_t;
    using Float = double;
    using String = std::string;
    using Array = std::vector<Node>;
    using Object = std::map<std::string, Node>;
    using Value = std::variant<Null, Bool, Int, Float, String, Array, Object>;
    struct Node {
        Value value;
        Node(Value _value) : value(_value) {}
        Node() : value(Null{}) {}
        auto& operator[](const std::string& key) {
            if (auto object = std::get_if<Object>(&value)) {
                return  (*object)[key];
            }
            throw std::runtime_error("not an object");
        }
        auto operator[](size_t index) {
            if (auto array = std::get_if<Array>(&value)) {
                return array->at(index);
            }
            throw std::runtime_error("not an array");
        }
        void push(const Node& rhs) {
            if (auto array = std::get_if<Array>(&value)) {
                array->push_back(rhs);
            }
        }
    };

    struct JsonParser {
        std::string_view json_str;
        size_t pos = 0;

        void parse_whitespace() {
            while (pos < json_str.size() && std::isspace(json_str[pos])) {
                ++pos;
            }
        }

        auto parse_null() -> std::optional<Value> {
            if (json_str.substr(pos, 4) == "null") {
                pos += 4;
                return Null{};
            }
            return{};
        }

        auto parse_true() -> std::optional<Value> {
            if (json_str.substr(pos, 4) == "true") {
                pos += 4;
                return true;
            }
            return {};
        }

        auto parse_false() -> std::optional<Value> {
            if (json_str.substr(pos, 5) == "false") {
                pos += 5;
                return false;
            }
            return {};
        }

        auto parse_number()->std::optional<Value> {
            size_t endpos = pos;
            while (endpos < json_str.size() && (
                std::isdigit(json_str[endpos]) ||
                json_str[endpos] == 'e' ||
                json_str[endpos] == '.')) {
                endpos++;
            }
            std::string number = std::string{ json_str.substr(pos, endpos - pos) };
            pos = endpos;
            static auto is_Float = [](std::string& number) {
                return number.find('.') != number.npos ||
                    number.find('e') != number.npos;
            };
            if (is_Float(number)) {
                try {
                    Float ret = std::stod(number);
                    return ret;
                }
                catch (...) {
                    return {};
                }
            }
            else {
                try {
                    Int ret = std::stoi(number);
                    return ret;
                }
                catch (...) {
                    return {};
                }
            }
        }

        auto parse_string()->std::optional<Value> {
            pos++;// "
            size_t endpos = pos;
            while (pos < json_str.size() && json_str[endpos] != '"') {
                endpos++;
            }
            std::string str = std::string{ json_str.substr(pos, endpos - pos) };
            pos = endpos + 1;// "
            return str;
        }
        auto parse_array()->std::optional<Value> {
            pos++;// [
            Array arr;
            while (pos < json_str.size() && json_str[pos] != ']') {
                auto value = parse_value();
                arr.push_back(value.value());
                parse_whitespace();
                if (pos < json_str.size() && json_str[pos] == ',') {
                    pos++;// ,
                }
                parse_whitespace();
            }
            pos++;// ]
            return arr;
        }

        auto parse_object() ->std::optional<Value> {
            pos++;// {
            Object obj;
            while (pos < json_str.size() && json_str[pos] != '}') {
                auto key = parse_value();
                parse_whitespace();
                if (!std::holds_alternative<String>(key.value())) {
                    return {};
                }
                if (pos < json_str.size() && json_str[pos] == ':') {
                    pos++;// ,
                }
                parse_whitespace();
                auto val = parse_value();
                obj[std::get<String>(key.value())] = val.value();
                parse_whitespace();
                if (pos < json_str.size() && json_str[pos] == ',') {
                    pos++;// ,
                }
                parse_whitespace();
            }
            pos++;// }
            return obj;

        }

        auto parse_value() ->std::optional<Value> {
            parse_whitespace();
            switch (json_str[pos]) {
            case 'n':
                return parse_null();
            case 't':
                return parse_true();
            case 'f':
                return parse_false();
            case '"':
                return parse_string();
            case '[':
                return parse_array();
            case '{':
                return parse_object();
            default:
                return parse_number();
            }
        }

        auto parse() ->std::optional<Node> {
            parse_whitespace();
            auto value = parse_value();
            if (!value) {
                return {};
            }
            return Node{ *value };
        }
    };


    auto parser(std::string_view json_str) ->std::optional<Node> {
        JsonParser p{ json_str };
        return p.parse();
    }


    class JsonGenerator {
    public:
        static auto generate(const Node& node) -> std::string {
            return std::visit(
                [](auto&& arg) -> std::string {
                    using T = std::decay_t<decltype(arg)>;
                    if constexpr (std::is_same_v<T, Null>) {
                        return "null";
                    }
                    else if constexpr (std::is_same_v<T, Bool>) {
                        return arg ? "true" : "false";
                    }
                    else if constexpr (std::is_same_v<T, Int>) {
                        return std::to_string(arg);
                    }
                    else if constexpr (std::is_same_v<T, Float>) {
                        return std::to_string(arg);
                    }
                    else if constexpr (std::is_same_v<T, String>) {
                        return generate_string(arg);
                    }
                    else if constexpr (std::is_same_v<T, Array>) {
                        return generate_array(arg);
                    }
                    else if constexpr (std::is_same_v<T, Object>) {
                        return generate_object(arg);
                    }
                },
                node.value);
        }
        static auto generate_string(const String& str) -> std::string {
            std::string json_str = "\"";
            json_str += str;
            json_str += '"';
            return json_str;
        }
        static auto generate_array(const Array& array) -> std::string {
            std::string json_str = "[";
            for (const auto& node : array) {
                json_str += generate(node);
                json_str += ',';
            }
            if (!array.empty()) json_str.pop_back();
            json_str += ']';
            return json_str;
        }
        static auto generate_object(const Object& object) -> std::string {
            std::string json_str = "{";
            for (const auto& [key, node] : object) {
                json_str += generate_string(key);
                json_str += ':';
                json_str += generate(node);
                json_str += ',';
            }
            if (!object.empty()) json_str.pop_back();
            json_str += '}';
            return json_str;
        }
    };

    inline auto generate(const Node& node) -> std::string { return JsonGenerator::generate(node); }


    auto  operator << (std::ostream& out, const Node& t) ->std::ostream& {
        out << JsonGenerator::generate(t);
        return out;
    }

    
}
using namespace json;


int main() {
    std::ifstream fin("json.txt");
    std::stringstream ss; ss << fin.rdbuf();
    std::string s{ ss.str() };
    auto x = parser(s).value();
    std::cout << x << "\n";
    x["configurations"].push({true});
    x["configurations"].push({Null {}});
    x["version"] = { 114514LL };
    std::cout << x << "\n";
}

跳表

内容

项目名称:基于跳表实现的轻量级KV存储

采用skiplist作为底层数据结构,支持插入,删除,查询等常见操作。

使用C++模板编程,使用类似STL,支持自定义类型与自定义比较函数(可以传入lambda与仿函数),迭代器遍历。

使用

{
    //使用lambda
    auto cmp = [](const string& a, const string& b) {return a.length() < b.length(); };
    skip_list < string, int, decltype(cmp)> list(cmp);
    list.insert("aab", 1321);
    list.insert("hello", 54342);
    list.insert("world", 544);
    for (auto it = list.begin(); it != list.end(); it++) {
        cout << it->key << " " << it->value << endl;
    }
}

cout << "==================================" << endl;

{
    //使用仿函数
    struct cmp {
        bool operator()(int a, int b) {
            return a > b;
        }
    };
    skip_list < int, int, cmp> list{};
    for (int i = 1; i <= 10; i++)list.insert(rand() % 20, rand());
    for (auto it = list.find(10); it != list.end(); it++) {
        cout << it->key << " " << it->value << endl;
    }
}

cout << "==================================" << endl;

{
    //默认小于号
    skip_list<int, int>list;
    list.insert(1, 3);
    list.insert(1, 3);
    list.insert(4, 3);
    list.insert(5, 3);
    list.insert(1, 3);
    list.insert(4, 3);
    for (auto it = list.begin(); it != list.end(); it++) {
        cout << it->key << " " << it->value << endl;
    }

}

输出

aab 1321

world 544

hello 54342

==================================

10 31181

9 8161

7 20531

6 12560

==================================

1 3

4 3

5 3

八股内容

采用skiplist作为底层数据结构

跳表是一种引入了随机化的数据结构。

实际上,你把网上找到的跳表的图片,斜着一下看,是不是很像一个BST呢(

对于这方面的内容,还是看看oiwiki吧

跳表 - OI Wiki

使用C++模板编程

C++模板学习实践

使用类似STL,支持自定义类型与自定义比较函数

严格鸽:现代C++学习——利用模板把函数当作参数

迭代器遍历

就是在类里面还有一个内部类

比如set<int>::iterator 就是 set<int> 这个类的内部的一个类

代码

#include<iostream>
#include<string>
#include<set>
#include<time.h>
using namespace std;
template<typename T>
struct Less {
    bool operator () (const T & a , const T & b) const {
        return a < b;
    }
};
template<typename K, typename V,typename Comp = Less<K>>
class skip_list {
private:
    struct skip_list_node {
        int level;
        const K key;
        V value;
        skip_list_node** forward;
        skip_list_node() :key{ 0 }, value{ 0 }, level{ 0 }, forward{0} {}
        skip_list_node(K k, V v, int l, skip_list_node* nxt = nullptr) :key(k), value(v), level(l) {
            forward = new skip_list_node * [level + 1];
            for (int i = 0; i <= level; ++i) forward[i] = nxt;
        }
        ~skip_list_node() { delete[] forward; }
    };
    using node = skip_list_node;
    void init() {
        srand((uint32_t)time(NULL));
        level = length = 0;
        head->forward = new node * [MAXL + 1];
        for (int i = 0; i <= MAXL; i++)
            head->forward[i] = tail;
    }
    int randomLevel() {
        int lv = 1; while ((rand() & S) < PS) ++lv;
        return MAXL > lv ? lv : MAXL;
    }
    int level;
    int length;
    static const int MAXL = 32;
    static const int P = 4;
    static const int S = 0xFFFF;
    static const int PS = S / P;
    static const int INVALID = INT_MAX;
    node* head, * tail;
    Comp less;
    node* find(const K& key, node** update) {
        node* p = head;
        for (int i = level; i >= 0; i--) {
            while (p->forward[i] != tail && less(p->forward[i]->key, key)) {
                p = p->forward[i];
            }
            update[i] = p;
        }
        p = p->forward[0];
        return p;
    }
public:
    struct Iter {
        node* p;
        Iter() : p(nullptr) {};
        Iter(node* rhs) : p(rhs) {}
        node* operator ->()const { return (p);}
        node& operator *() const { return *p;}
        bool operator == (const Iter& rhs) { return rhs.p == p;}
        bool operator != (const Iter& rhs) {return !(rhs.p == p);}
        void operator ++() {p = p->forward[0];}
        void operator ++(int) { p = p->forward[0]; }
    };
   
    skip_list() : head(new node()), tail(new node()), less{Comp()} {
        init();    
    }
    skip_list(Comp _less) : head(new node()), tail(new node()),  less{_less} {
        init();
    }
    void insert(const K& key, const V& value) {
        node * update[MAXL + 1];
        node* p = find(key,update);
        if (p->key == key) {
            p->value = value;
            return;
        }
        int lv = randomLevel();
        if (lv > level) {
            lv = ++level;
            update[lv] = head;
        }
        node * newNode = new node(key, value, lv);
        for (int i = lv; i >= 0; --i) {
            p = update[i];
            newNode->forward[i] = p->forward[i];
            p->forward[i] = newNode;
        }
        ++length;
    }

    bool erase(const K& key) {
        node* update[MAXL + 1];
        node* p = find(key, update);
        if (p->key != key)return false;
        for (int i = 0; i <= p->level; ++i) {
            update[i]->forward[i] = p->forward[i];
        }
        delete p;
        while (level > 0 && head->forward[level] == tail) --level;
        --length;
        return true;
    }
    Iter find(const K&key) {
        node* update[MAXL + 1];
        node* p = find(key, update);
        if (p == tail)return tail;
        if (p->key != key)return tail;
        return Iter(p);
    }
    bool count(const K& key) {
        node* update[MAXL + 1];
        node* p = find(key, update);
        if (p == tail)return false;
        return key == p->key;
    }
    Iter end() {
        return Iter(tail);
    }   
    Iter begin() {
        return Iter(head->forward[0]);
    }
};
int main()
{
    {
        //使用lambda
        auto cmp = [](const string& a, const string& b) {return a.length() < b.length(); };
        skip_list < string, int, decltype(cmp)> list(cmp);
        list.insert("aab", 1321);
        list.insert("hello", 54342);
        list.insert("world", 544);
        for (auto it = list.begin(); it != list.end(); it++) {
            cout << it->key << " " << it->value << endl;
        }
    }

    cout << "==================================" << endl;
    
    {
        //使用仿函数
        struct cmp {
            bool operator()(int a, int b) {
                return a > b;
            }
        };
        skip_list < int, int, cmp> list{};
        for (int i = 1; i <= 10; i++)list.insert(rand()%20, rand());
        for (auto it = list.find(10); it != list.end(); it++) {
            cout << it->key << " " << it->value << endl;
        }
    }

    cout << "==================================" << endl;

    {
        //默认小于号
        skip_list<int, int>list;
        list.insert(1, 3);
        list.insert(1, 3);
        list.insert(4, 3);
        list.insert(5, 3);
        list.insert(1, 3);
        list.insert(4, 3);
        for (auto it = list.begin(); it != list.end();it++) {
            cout << it->key << " " << it->value << endl;
        }
       
    }

    {
        //可以添加 T && 实现move语义
        //添加重载 []
    }
    
}


线程池

内容

项目名称:简易线程池(C++17)

实现多线程安全的任务队列,线程池使用异步操作,提交(submit)使用与thread相同。

内部利用完美转发获取可调用对象的函数签名,lambda与function包装任务,使用RAII管理线程池的生命周期。

使用

{
    ThreadPool pool(8);
    int n = 20;
    for (int i = 1; i <= n; i++) {
        pool.submit([](int id) {
            if (id % 2 == 1) {
                this_thread::sleep_for(0.2s);
            }
            unique_lock<mutex> lc(_m);
            cout << "id : " << id << endl;
            }, i);
    }
}

八股内容

首先是多线程相关的,这里我推荐mq老师的视频

线程池的设计

Skykey:基于C++11实现线程池

实现多线程安全的任务队列

其实就是给queue加锁,这里使用了C++17的共享锁来实现类似读写锁的作用。

std::shared_mutex - cppreference.com

线程池使用异步操作

异步操作示例

packaged_task<int(int, int)> func([](int a, int b) {return a + b; });
auto ret = func.get_future();

thread t1{ [&]() {
         {unique_lock<mutex> lc(_m); cout << "======1=======\n"; }
         this_thread::sleep_for(2s);
         func(3, 5);
         {unique_lock<mutex> lc(_m); cout << "======1=======\n"; }
} };

thread t2{ [&]() {
        {unique_lock<mutex> lc(_m); cout << "======2=======\n"; }
        cout << ret.get() << "\n";
        {unique_lock<mutex> lc(_m); cout << "======2=======\n"; }
} };
{
    t1.join();
    t2.join();
}


输出

======2=======

======1=======

======1=======

8

======2=======

我们就是用的这个来把任务交给线程池,然后执行的

内部利用完美转发获取可调用对象的函数签名

李超:聊聊C++中的完美转发

lambda与function包装任务

严格鸽:c++函数进化史 (函数,函数指针,function,仿函数,lambda)

严格鸽:现代C++学习——实现一个std::function

RAII管理线程池的生命周期

就是普通的RAII了

严格鸽:现代C++学习—— 什么是RAII

代码

#include <iostream>
#include<thread>
#include<mutex>
#include <chrono>
#include<time.h>
#include<vector>
#include<queue>
#include<future>
#include <mutex>
#include <queue>
#include <functional>
#include <future>
#include <thread>
#include <utility>
#include <vector>
#include <condition_variable>
#include<string>
#include< shared_mutex>
using namespace std;

template<typename T>
struct safe_queue {
    queue<T>que;
    shared_mutex _m;
    bool empty() {
        shared_lock<shared_mutex>lc(_m);
        return que.empty();
    }
    auto size() {
        shared_lock<shared_mutex>lc(_m);
        return que.size();
    }
    void push(T& t) {
        unique_lock<shared_mutex> lc(_m);
        que.push(t);
    }
    bool pop(T& t) {
        unique_lock<shared_mutex> lc(_m);
        if (que.empty())return false;
        t = move(que.front());
        que.pop();
        return true;
    }
};
class ThreadPool {
private:
    class worker {
    public:
        ThreadPool* pool;
        worker(ThreadPool* _pool) : pool{ _pool } {}
        void operator ()() {
            while (!pool->is_shut_down) {
                {
                    unique_lock<mutex> lock(pool->_m);
                    pool->cv.wait(lock, [this]() {
                        return this->pool->is_shut_down ||
                            !this->pool->que.empty();
                        });
                }
                function<void()>func;
                bool flag = pool->que.pop(func);
                if (flag) {
                    func();
                }
            }
        }
    };
public:
    bool is_shut_down;
    safe_queue<std::function<void()>> que;
    vector<std::thread>threads;
    mutex _m;
    condition_variable cv;
    ThreadPool(int n) : threads(n), is_shut_down{ false } {
        for (auto& t : threads)t = thread{ worker(this) };
    }
    ThreadPool(const ThreadPool&) = delete;
    ThreadPool(ThreadPool&&) = delete;
    ThreadPool& operator=(const ThreadPool&) = delete;
    ThreadPool& operator=(ThreadPool&&) = delete;

    template <typename F, typename... Args>
    auto submit(F&& f, Args &&...args) -> std::future<decltype(f(args...))> {
        function<decltype(f(args...))()> func = [&f, args...]() {return f(args...); };
        auto task_ptr = std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);
        std::function<void()> warpper_func = [task_ptr]() {
            (*task_ptr)();
        };
        que.push(warpper_func);
        cv.notify_one();
        return task_ptr->get_future();
    }
    ~ThreadPool() {
        auto f = submit([]() {});
        f.get();
        is_shut_down = true;
        cv.notify_all(); // 通知,唤醒所有工作线程
        for (auto& t : threads) {
            if (t.joinable()) t.join();
        }
    }
};
mutex _m;
int main()
{

    ThreadPool pool(8);
    int n = 20;
    for (int i = 1; i <= n; i++) {
        pool.submit([](int id) {
            if (id % 2 == 1) {
                this_thread::sleep_for(0.2s);
            }
            unique_lock<mutex> lc(_m);
            cout << "id : " << id << endl;
            }, i);
    }
}

全部评论
啥样才算是有价值的项目啊
8 回复 分享
发布于 2023-03-11 19:15 山东
第二个刚好在知乎看到,已经被人拿来买课了
4 回复 分享
发布于 2023-03-10 17:41 广东
ygg牛牛牛😍
3 回复 分享
发布于 2023-03-10 17:50 吉林
感谢大佬分享
2 回复 分享
发布于 2023-03-11 18:51 江西
114515(警撅
2 回复 分享
发布于 2023-03-11 22:01 安徽
我记得我之前在知乎看过这个
2 回复 分享
发布于 2023-03-23 15:36 广西
感谢分享
1 回复 分享
发布于 2023-03-18 22:18 湖北
又是ygg😍
1 回复 分享
发布于 2023-03-23 20:50 山东
不如楼主也开个课吧,我买
1 回复 分享
发布于 2023-03-25 21:59 广东
🐮
1 回复 分享
发布于 2023-03-27 13:15 广东
1 回复 分享
发布于 2023-04-03 08:33 陕西
柚子厨捏
1 回复 分享
发布于 2023-04-19 08:18 江西
感谢大佬分享
点赞 回复 分享
发布于 2023-03-10 17:19 安徽
其实可以直接std async会方便一点(
点赞 回复 分享
发布于 2023-03-11 05:38 美国
看这位大佬的帖子,学到了很多东西,感谢
点赞 回复 分享
发布于 2023-03-12 21:37 湖南
点赞 回复 分享
发布于 2023-03-13 07:46 辽宁
感谢大神分享
点赞 回复 分享
发布于 2023-03-15 02:41 天津
感谢感谢
点赞 回复 分享
发布于 2023-03-17 08:51 上海
线程池当年我也写过😂
点赞 回复 分享
发布于 2023-04-02 11:18 陕西
没有注释看着好费劲
点赞 回复 分享
发布于 2023-05-05 11:22 四川

相关推荐

找工作的终极目标:谈薪!谈高薪!兄弟们咱们别给多少钱都干,第一影响市场行情&nbsp;第二对于自己来说越高的薪资&nbsp;代表自己越好的生活水平第一点:重点(HR的定位)记住:你不是在和给你打电话的HR谈,只是让这个HR把你的诉求上报上去,所以别把HR当敌人,统一战线,为己所用。我们与HR之间并没有太大的利益关系,你多1k、2k不会太影响HR,所以我们并没有直接的交易关系,大多数的薪资一般HR没有决定权,只有申报权,可能有个界定,如果小于岗位预期可能HR就可以定,大于需要申报,这个区分各个公司,不过首先我们需要正视HR,摆正你和他的关系是你谈薪的一个好的开始。1.&nbsp;谈薪就是——讨价还价&nbsp;&nbsp;谈薪谈薪,重点是“谈”。应届毕业生普遍是学生思维:一锤子买卖,给的低了立马回绝,给的稍稍觉得还可以接受就喜形于色,很容易被精明的HR捕捉到信息进而压价。&nbsp;&nbsp;找工作的本质是什么?出卖自身的劳动力,和市场上卖东西的没什么区别,谈价还价是常态,一定要有耐心。&nbsp;&nbsp;既然如此,那么问题就转化为:怎么讨价还价?2.&nbsp;谈价还价指导思想:货比三家&nbsp;&nbsp;市场买东西压价怎么压?&nbsp;&nbsp;我朋友前段时间去买电动车:(1)先在网上看价格;(2)以网上的价格去问A家最低价,在A家得到一个基础价格3000元,并且赠送雨衣头盔,但是电动车上牌要再花100元;(3)在B家问差不多配置的车,给B家说A家最低2950,送雨衣送头盔还给免费上牌,拿到B家的最低价2850;(4)在C家说A家2900、B家2800,这时候底气就比较足了,直接问C家最低价多少就行了,这时候C家不降价,还是给出了最低3000的价格;(5)骚操作来了,折返回说B家、C家都是最低2800,但是车型感觉不如你家好看,你家最低多少,2800的话我就在你家拿了;最终:成功在A家低价买到心仪车。所以,你应该学会怎么谈薪了吧:(1)网上看看前两年的价格,参考去年和前年的价格,包括行业整体薪资水平、你要谈薪的这家公司的水平。这些信息哪里去看,我们每年都有统计薪资情况,咱们群、*********都可以,芯片类、FPGA、软件、嵌入式的。(2)去年的价格仅供参考,今年的行业环境相对比去年可能要弱一些,所以在此基础上可以将心理预期暂时先调低2k,拿第一家公司试水,先拿到一个行业在今年的价格,比如18k、20k或者23k;(3)有了参考价,第二家的时候就可以“谈”了,你知道了1家公司的价格是吧,但是他们不知道啊,你完全可以虚拟出2-3个公司,就说A给了我20k、B给了我22k,来拿第二家公司的价格;(4)后面依此类推了,谈的高不高,在于你要厚脸皮、要包装自己,另外还在于你有几个offer;3.&nbsp;讨价还价之——如何谈第一家&nbsp;&nbsp;万事开头难,根据前面的案例我们可以知道,最难是第一家。(1)先拿公司的底价&nbsp;&nbsp;有的公司比较实诚,直接给你开出来一个价格,甚至会在校招宣传时直接说出25w+、30w+、22k*14-16等,这种就直接有了一个基础底价,再参考下往年的sp的价格,在他们开出的价格上谈1-2k还是可以的;&nbsp;&nbsp;有的公司绕来绕去都不说自己能给多少,一直先让你说期望薪资的,慎重一点,提前做好功课,这时候就要先去*********、交流群、知乎上去看看情况。(2)期望薪资怎么说&nbsp;&nbsp;有的HR很鸡贼,一定要先问出你的期望薪资,本来可能给你的薪资范围是20-23k,但是你说期望薪资20k以上,可能就只给你20k而不会往上。这时候就要先提前了解去年给的白菜是18k+、sp是22k+,年终奖有几个月?公积金多少?HR问你期望薪资的时候,你就要先反问薪资结构、多久调薪一次、调薪幅度、年终、有没有食堂、班车、加班费、加班情况,即使说了期望薪资20k以后,也可以再以他们没食堂花销大为理由要求+1k工资;(话术1:xx公司也给我20k,但是有餐补还有食堂,每个月吃饭基本不花钱);(话术2:xx公司也是20k/月,但是年终奖保底3个月);(话术3:xx公司也是20k/月,但是给我2万签字费----至于给没给谁知道呢);(3)薪资结构+福利待遇要问清&nbsp;&nbsp;很多同学只关心工资和年终奖,实际上绩效怎么考核、加班情况怎样、有没有餐补、有没有食堂、有没有班车、班车是否收费、有没有宿舍、有没有房补、工作地有没有人才补贴、几点上班、午休时间等都是非常影响工作的幸福感的,像交通和吃饭也是实打实的花销,有食堂有餐补有班车相比没有的可能每月生活费能省下1k-1.5k,而且避免了挤地铁,吃的也放心。(4)回复话术,不要一锤子买卖&nbsp;&nbsp;以往线下谈薪,有同学觉得给的低了扭头就走,或者线上直接挂电话了,如果手里offer太多或者已经有满意offer那无可厚非,无关还是想拿到当下这个offer,不妨换一种说法:我也很想去,但是你们这边给的薪资想起其他公司竞争力不太够~我回去再考虑一下,咱这边在薪资方面还不足够吸引~xx给我多少,咱这边如果能给到多少我举得还是咱这边更有吸引力;咱这边薪资是怎么评定的,我当时简历上有xx专利/奖项没写,能不能再申请一下加1k;附:新人问题汇总精华,干货不能再干了:1.五险一金养老保险、医疗保险、失业保险、工伤保险、生育保险和住房公积金,最重要的是公积金,这里公积金有几个问题!公积金:公司全额缴纳?还是不全额缴纳,是以多少比例缴纳,假如以1万为基础最低是百分之5&nbsp;最高12也就是&nbsp;最低是&nbsp;500&nbsp;&nbsp;最高1200&nbsp;&nbsp;公司百分之50&nbsp;个人百分之50,最低与最高差距&nbsp;1200+1200-(500+500)=1400注:不全额缴纳的公司可以去公积金管理中心举报公司补缴,个人部分可不交,只让公司补缴!2.公司作息:我觉得这个对于我来讲可能很重要,如果你在北京,可能你的通勤会在30-1小时内,所以我还是挺在乎公司的作息和打卡,不过有的公司不打卡。3.年包?年终奖这是个很迷的话题,基本上百分之99的公司不会把年终奖写在合同上,所以,这是未知数,也就是年终奖的把握权会在公司,所以有的小伙伴在算工资的时候会把自己的年终奖放在自己的薪资结构里,我只能说,如果那天公司真的亏损严重或者绩效过低你可能一点也没有,所以年终奖也好&nbsp;受影响的因素是最大的,一般以部门同事的答案为准,HR的一般只做参考4.公司背调这个事情是必须要做的,所有的公司哪怕大公司也好小公司也好,都要做好对自己的部门和公司的一些背景调查,公司身上有没有拖欠员工工资,或者是有没有一些其他的民事案件,部门的年终绩效和工作强度这也是你谈OFFER之前要做的。5.保护自己的权益:这也是我们应届生进入社会的第一课,多读书多看报少吃零食多睡觉,这个社会的规则就是法律,我们对于一些公积金少缴纳,工资延迟发送,不正常双休,偷税漏税,不签劳动合同,我觉得我门应该多了解了解,不要觉得这些东西离我们很远,或许某天你就能用得到,不过也不要过于担心,所有的违规行为都会有相应的惩罚,所以我们作为打工人,我觉得要学的第一课就是保护自己的打工人权益,当你在得到大树的庇护时才能更好的汲取营养。6.对了还有一点:裁员这个怎么说呢,我希望大家不被裁员,哈哈哈,但是环境这样我也不勉嘱咐大家几句。裁员怎么说呢,无非现在的公司都想不给赔偿就走人那么接下来我来模拟一下可能你会遇到的问题。1.竞业协议:这个问题呢,竞业协议&nbsp;对于有些行业&nbsp;HR或者公司逼着你签这个,这个时候呢不要签,第一,签之前需要问清楚赔偿的金额第二这个呢如果协议有坑&nbsp;你后边找工作会是一个大麻烦。2.企业会以什么样的方式裁你?裁员讲究快准狠,这是企业的裁人方案,我希望各位同学被裁时不要急着签一些合同也好,经济纠纷解除的一些协议也好,企业讲究的就是防止夜长梦多,所以你不要着急,稳定心态,仔细算算自己的赔偿金,确保赔偿金额正确,不正确及时进行反馈和谈判,就一个点,保证自己的赔偿金额准确无误就OK。不建议就是能要n+1要&nbsp;n+2,能要n+2&nbsp;要2n可以试探但必须懂得隐忍,否则就陷入劳动仲裁,哎&nbsp;伤心伤肺!嵌入式C++面经推荐大佬面经&nbsp;&nbsp;链接在下边&nbsp;&nbsp;c++/嵌入式面经专栏-牛客网 https://www.nowcoder.com/creation/manager/columnDetail/MJNwoM
点赞 评论 收藏
分享
评论
208
1504
分享
牛客网
牛客企业服务