• 三道题(关于虚表指针位置/合成64位ID/利用栈实现四则运算)


    第一题 C++标准中,虚表指针在类的内存结构位置没有规定,不同编译器的实现可能是不一样的。请实现一段代码,判断当前编译器把虚表指针放在类的内存结构的最前面还是最后面。

     第二题 在游戏中所有物品的实例都有一个唯一的ID,用于区分这个物品的唯一性。每个物品的ID在物品产生时就被分配了,此后将永远不会改变。每个服务器具有一个服务器ID,不同的服务器ID均不会重复,其值在0-65535之间。请设计一个生成64位的物品ID的算法,算法要求任何时刻、任何不同的服务器中生成的物品ID均不会重复。

     第三题 在游戏服务端与客户端的通信中,数据的正确性是非常重要的。实际运营中发现,有一些玩家使用工具重复发送数据包,比如领取奖励的请求,发2个同样的数据包到服务器;有的玩家会篡改数据,自己随意制造一个数据包发给服务器,给服务器带来的隐患更大。为了避免这种情况,如果是你开发通信协议,如何避免重复发包,以及给逻辑数据包进行加密处理。提示:避免重复发包可以给每个数据包进行自增的编号,数据校验可以使用crc等算法产生冗余数据附加在数据头部,但是需要考虑如何做到不被轻易破解。请参考各种资料结合自己的经验编码实现;

     第四题 请实现一个数学表达式计算器,计算器需要支持加、减、乘、除以及括号优先的数学运算。例如,能够对表达式“(1 + 2) * (3 / (5 - 3.2))”计算结果。

     解题思路:

    第一题:虚表指针在类的内存结构是在前面还是在后面,我们注意到,仅仅简单的存在成员变量和虚函数的简单类中,类的内存结构包括成员变量的空间和虚函数表指针的空间。这种顺序自然成为虚表指针和成员变量位置的先后顺序。如下图所示。①当虚表指针存在类内存结构的前面的时候,自然成员变量的位置会产生类地址的偏移量,类实例的地址和类中第一个成员变量的地址是不相等的。②当虚表指针存在类内存结构的后面的时候,类实例的地址和类中第一个成员变量的地址是相等的。因此,我们只需要定义仅有成员变量和虚函数的简单类,之后实例化类,比较类实例地址和类实例中第一个成员变量的地址,若相等,则虚表指针是放置在类内存结构后面的,否则在前面。

    /*
    判断当前编译器把虚表指针放在类的内存结构的最前面还是最后面
    */
    
    #include<iostream>
    using namespace std;
    
    //测试类
    class CMyClass
    {
    public:
        int n; //成员变量
        //虚函数
        virtual void Foo(void){}
        virtual void Hoo(void){}
    };
    
    
    
    void main()
    {
        CMyClass myClass; //定义类实例
        //指针类型转换
        char *p1 = reinterpret_cast<char*>(&myClass);
        char *p2 = reinterpret_cast<char*>(&myClass.n);
        if(p1 == p2) //若类实例地址和类第一个成员变量地址相等,则虚函数表地址放在类内存后面
        {
            cout<<"vPtr is in the end of class instance!"<<endl;
        }
        else //否则在类内存前面
        {
            cout<<"vPtr is in the head of class instance!"<<endl;
        }
    
    }

    第二题:在多台服务器上要生成唯一的64位的游戏物品ID,游戏服务器有唯一的serverId编号(0-65535),用16位足够保存,因此可以考虑用高16位来保存服务器id,用低48位来保存单个服务器上的服务器物品计数,提供唯一的服务器ID,再提供服务器上的递增的服务器计数值,两者合成64位的物品ID,则在多台服务器中,这个ID绝对是唯一的,可以在本地服务器就生成唯一的物品ID,从而解决多台服务器之间的数据同步问题。如下图:

    /*
    在多台服务器上面生成唯一的游戏物品ID
    */
    #include<iostream>
    using namespace std;
    void main()
    {
        unsigned long long itemId;//游戏物品ID
        unsigned long long itemCount;
        while(1)
        {
            cout<<"请输入服务器ID(0-65535)"<<endl;
            cin>>itemId;
            if(itemId >= 0 && itemId <= 65535)//保证服务器ID输入正确(16位正整数)
                break;
        }
        itemId <<= 48;  //将服务器ID号置高16位
        while(1)
        {
            cout<<"请输入当前服务器物品个数ID(0-65535)"<<endl;
            cin>>itemCount;
            if(itemCount >= 0 && itemCount <= 0xffffffffffff)//保证服务器物品个数不会超出(48位正整数)
                break;
        }
        itemId += itemCount; //服务器号和物品递增号组合
        cout<<"即时生成的服务器物品ID :"<<itemId<<endl;
    }

    第四题:要实现一个简单的数学表达式的运算,其关键在于运算符优先级的问题,同时要保存优先级低的运算符和之前的运算数,及时计算优先级高的运算符的运算,且保存结果。

    在此,对于运算数可以通过实现一个double运算数栈来保存需要运算的运算数,通过一个运算符栈来保存读入的运算符,将优先级低的运算符压栈,及时运算优先级高的运算。

    下图是运算符的优先级表:

    /************************************************************************/
    /*  实现一个数学表达式计算器                                                                    */
    /************************************************************************/
    #include<iostream>
    #include<stack>
    #include<string>
    #include<stdlib.h>
    #include<iomanip>
    using namespace std;
    
    //************************************
    // Method:    isOperator
    // FullName:  isOperator
    // Access:    public 
    // Returns:   bool
    // 判断解析出来的字符是否是运算符
    // Parameter: char c 传入的运算字符
    //************************************
    bool isOperator(char c)
    {
        if(c == '+'||c == '-'||c == '*'||c == '/'||c == '('||c == ')')
            return true;
        return false;
    }
    
    //************************************
    // Method:    compareOperator
    // FullName:  compareOperator
    // Access:    public 
    // Returns:   char   a运算符优先级高则返回'>',a运算符优先级低则返回'<',两者优先级相等则返回'='
    // Qualifier: 比较运算符a和运算符b的优先级
    // Parameter: char a 
    // Parameter: char b
    //************************************
    char compareOperator(char a,char b)
    {
        if(a == '+'||a == '-')  
        {
            if(b == '+'||b == '-')
                return '>';
            else if(b == '*'||b == '/')
                return '<';
            else if(b == ')')
                return '>';
            else if(b == '(')
                return '<';
            else
                return '>';
        }
        else if(a == '*'||a == '/')
        {
            if(b =='+'||b == '-')
                return '>';
            else if(b == '*'||b == '/')
                return '>';
            else if(b == ')')
                return '>';
            else if(b=='(')
                return '<';
            else
                return '>';
        }
        else if(a == '(')
        {
            if(b == ')')
                return '=';
            else
                return '<';
        }else if(a == ')')
            return '>';
        return '<';
    
    }
    
    //************************************
    // Method:    OperatorNum
    // FullName:  OperatorNum
    // Access:    public 
    // Returns:   double
    // Qualifier:   计算数a和数b通过运算符operatorC的结果
    // Parameter: double a   运算数a
    // Parameter: double b   运算数b
    // Parameter: char operatorC   运算符
    //************************************
    double OperatorNum(double a,double b,char operatorC)
    {
        if(operatorC=='+')
            return a+b;
        else if(operatorC=='-')
            return a-b;
        else if(operatorC=='*')
            return a*b;
        else
            return a/b;
    }
    
    //算数表达式计算函数
    //************************************
    // Method:    myCalc
    // FullName:  myCalc
    // Access:    public 
    // Returns:   void
    // Qualifier:   计算算术表达式expressionStr的结果
    // Parameter: string & expressionStr  传入的算术表达式expressionStr
    //************************************
    void myCalc(string &expressionStr)
    {
        size_t i; //定义遍历字符串变量i
        size_t j; //定义遍历字符串变量j
        stack<double> NumStack;   //定义操作数栈
        stack<char> operatorStack; //定义操作符栈
        operatorStack.push('#'); //初始化运算符栈
        for(i = 0;i < expressionStr.size();i++)
        {
            if(!isOperator(expressionStr[i]))  //判断是运算数的时候,解析出来运算数并压栈
            {
                string temStr="";
                for(j=i;j<expressionStr.size()&&!isOperator(expressionStr[j]);j++)
                {
                    temStr+=expressionStr[j];
                }
                double numDouble=atof(temStr.data()); //字符串转换成double
                NumStack.push(numDouble); //数字压栈
                i=--j;
            }
            else //字符是运算符的时
            {
                char operatorC=expressionStr[i];
                if(compareOperator(operatorStack.top(),operatorC)=='>') //栈顶运算符优先级高过当前运算符的优先级时,需要及时运算
                {
                    double a=NumStack.top();
                    NumStack.pop();
                    double b=NumStack.top();
                    NumStack.pop();
                    double c=OperatorNum(b,a,operatorStack.top());
                    operatorStack.pop();
                    NumStack.push(c);
                    i--;
    
                }
                else if(compareOperator(operatorStack.top(),operatorC)=='<') //栈顶运算符优先级低于当前运算符的优先级时,运算符压栈
                {
                    operatorStack.push(operatorC);
                }
                else //两者优先级相等的时候,需要弹出栈顶运算符
                {
                    operatorStack.pop();
                }
            }
        }
        //计算运算数栈中剩余下的结果数字
        for(i=0;i<operatorStack.size();i++)
        {
            double a=NumStack.top();
            NumStack.pop();
            double b=NumStack.top();
            NumStack.pop();
            double c=OperatorNum(b,a,operatorStack.top());
            operatorStack.pop();
            NumStack.push(c);
        }
        cout<<"The result is:"<<NumStack.top()<<endl; //运算数的栈顶即是最终结果
    }
    void main()
    {
        string expressionStr; //定义输入算术表达式字符串
        while(1)
        {
            cout<<"Please input the new Expression"<<endl;
            getline(cin,expressionStr);
            if(expressionStr=="")
                break;
            myCalc(expressionStr); //输入且计算输出
        }
    }
  • 相关阅读:
    [hdu-2048] 神、上帝以及老天爷
    or1200中IMMU分析(续)
    Java Applet读写client串口——终极篇
    树莓派_Linux串口编程_实现自发自收
    2014百度实习生面试题(部分)具体解释
    Leetcode
    eclipse+webservice开发实例
    现有一些开源ESB总线的比較
    《HTML5 从入门到精通--7.6.3 单元格垂直跨度——rowspan》
    百度究竟是哪国的公司
  • 原文地址:https://www.cnblogs.com/poemCoder/p/3228429.html
Copyright © 2020-2023  润新知