const 和non-const成员函数转化

本文探讨了如何在C++中通过static_cast和const_cast处理const和non-const对象间的切换,避免const方法的重复编写,同时解释了为何不直接用const调用non-const的原因。通过实例演示了如何在arr类中实现const和非const版本的operator[]方法。

问题背景:这是针对同一个类的const对象和non-const对象来说的,由于一个const对象只能调用const成员函数,因此有的时候需要同时实现针对const对象和non-const对象的 const方法和non-const方法,大部分时候有着实质等价实现,可以使用non-const版本调用const版本避免代码重复,然后在non-const中再添加自己的内容。

什么是const成员函数,在参数列表后有const关键字的方法为const方法;

解决方案:使用static_cast<const T1>和const_cast<T2>来实现const和非const的切换,这里使用的是non_const调用const版本。

方案问题:为什么不用const调用非const,因为这就会违背const不改变其对象的逻辑,因为非const没有承诺不会改变内容

详细方案:先使用static_cast 将*this从原始类型转化为const类型,再调用函数,使用const_cast移除函数返回值的const性质

代码案例:

#include "iostream"
using namespace std;
class arr{
public:
    int *a;
    int size;
    arr(int size_,int initc){
        a=new int[size_];
        size=size_;
        for(int i=0;i<size_;i++)
            *(a+i)=initc;
        
    };
    ~arr(){
        delete [] a;
        a=nullptr;
        size =0;
#include<iostream> #include<string> #include<cmath> using namespace std; template<typename T> class Stack; template<typename T> class Node { private: T m_data; Node* m_next = nullptr; friend class Stack<T>; public: Node(const T& val) :m_data(val) {} T& date() { return m_data; } //接口函数是很关键的存在,见SList中对<<重载运算符的定义 const T& date() const { return m_data; } //const函数是限定其不能改变类的数据成员,且如果一个对象被声明为 const,则只能调用它的 const 成员函数 Node* next() { return m_next; } Node(const Node&) = delete; Node& operator=(const Node&) = delete; }; template<typename T> class Stack { private: Node<T>* m_top = nullptr; public: Stack() = default; // Stack(const Stack&) = delete; Stack& operator = (const Stack&) = delete; ~Stack() { clean(); } const T& top() { if (empty()) { throw runtime_error("Stack is empty"); } return m_top->m_data; } //其实不返回引用也可以吧 void push(const T &val); void pop(); bool empty() { return m_top == nullptr; } void clean(); }; template<typename T> void Stack<T>::push(const T &val) { Node<T>* node = new Node<T> (val); node->m_next = m_top; m_top = node; } template<typename T> void Stack<T>::pop() { if (empty())return; Node<T>* p = m_top; m_top = m_top->m_next; delete p; } template<typename T> void Stack<T>::clean() { Node<T>* p = nullptr; while (m_top != nullptr) { p = m_top; m_top = m_top->m_next; delete p; } } class Calculator { private: Stack <double> m_num; Stack <char> m_opr; int precedence(const char& s) const; double readNum(string::const_iterator &it); //迭代器就是一种访问string中的各个元素的机制,类似于指针(其底层实现就是指针,同样有解引用(得到对应的数据)、++、- -的操作); bool isNum(string::const_iterator &c)const { //const_iterator是针对const对象的特例化 return *c >= '0' && *c <= '9' || *c == '.'; } void calculate(); public: Calculator() { m_opr.push('#'); } double doit(const string& exp); }; int Calculator::precedence(const char& s) const { switch (s) { case '=':return 0; case'#':return 1; case'(':return 1; case')':return 2; case'+':case'-':return 3; case'*':case'/':return 4; case '^': case '√': case 'L': return 5; } } double Calculator::readNum(string::const_iterator& it) { string t; while (isNum(it)) t += *it++; //对string对象实现拼接,并对迭代器it向后迭代 return stod(t); //string的函数,此条件下只对一个双精度数据处理 } /*int main() //std对两个数据的处理 { std::string numStr = "5646545.32 3.1415923"; size_t idx = 0; double number1 = std::stod(numStr, &idx); double number2 = std::stod(numStr.substr(idx)); getchar(); return 0; }*/ void Calculator::calculate() { char op = m_opr.top(); m_opr.pop(); if (m_opr.top() == '+' || m_opr.top() == '-' || m_opr.top() == '*' || m_opr.top() == '/') { double b = m_num.top(); m_num.pop(); double a = m_num.top(); m_num.pop(); switch (op) { case '+': m_num.push(a + b); break; case '-': m_num.push(a - b); break; case '*': m_num.push(a * b); break; case '/': if (b == 0) throw runtime_error("Division by zero"); m_num.push(a / b); break; } } else { double c = m_num.top(); m_num.pop(); switch (op) { case '^': m_num.push(c * c); break; case '√': if (c < 0) throw runtime_error("Square root of negative number"); m_num.push(sqrt(c)); break; case 'L': if (c <= 0) throw runtime_error("Logarithm of non-positive number"); m_num.push(log(c)); break; case '1': if (c == 0) throw runtime_error("Division by zero"); m_num.push(1.0 / c); break; } } } double Calculator::doit(const string& exp) { m_num.clean(); for (auto it = exp.begin(); it != exp.end();) { if (isNum(it)) m_num.push(readNum(it)); else { //单目运算符 char op = *it; if (op == '^' && (it + 1) != exp.end() && *(it + 1) == '2') { op = '^'; ++it; } else if (op == '1' && (it + 1) != exp.end() && *(it + 1) == '/' && (it + 2) != exp.end() && *(it + 2) == 'x') { op = '1'; it += 2; } else if (op == 'L' && (it + 1) != exp.end() && *(it + 1) == 'n') { op = 'L'; ++it; } // 括号 if (op == '(') { m_opr.push(op); ++it; continue; } else if (op == ')') { while (m_opr.top() != '(') { if (m_opr.top() == '#') { throw runtime_error("Mismatched parentheses"); } calculate(); } m_opr.pop(); // 弹出左括号 ++it; continue; } while (precedence(*it) <= precedence(m_opr.top())) { if (m_opr.top() == '#') break; calculate(); } if (*it != '=') m_opr.push (*it); ++it; //为啥 继续扫描要放在这里 } } while (m_opr.top() != '#') { if (m_opr.top() == '(') { throw runtime_error("Mismatched parentheses"); } calculate(); } return m_num.top(); } 分析为何无法实现2*(2+2)的计算,以及单目运算符的完成情况
最新发布
05-18
### 问题分析 #### 关于 `2*(2+2)` 的计算失败原因 在引用的内容中,未提及如何处理带有括号的复合表达式。通常情况下,如果计算器仅实现了基础的四则运算而忽略了优先级规则或括号解析逻辑,则可能导致此类错误[^1]。 具体来说,在标准的数学表达式求值过程中,需要遵循以下原则: - **操作符优先级**:乘法除法优先于加法减法。 - **括号优先级最高**:任何位于括号内的子表达式应被先计算。 然而,当前代码片段并未展示完整的表达式解析器逻辑。例如,`evaluateExpression()` 函数可能仅仅将字符串转换为数值并返回,而不考虑操作符优先级或括号的存在[^1]。因此,当遇到像 `2*(2+2)` 这样的复杂表达式时,由于缺少对括号的支持,可能会导致计算结果不正确。 --- #### 单目运算符(如平方、开方等)的实现情况 根据引用[2]中的代码示例,可以看到部分单目运算符已实现,比如平方 (`^`) 立方 (`v`)。以下是其工作原理: - 平方运算通过检测连续两次输入 `'^'` 来触发,并调用 `pow(base, exponent)` 实现幂运算[^2]。 - 开方运算虽然未显式提到,但在引用[3]中提到了支持 `sqrt(x)` 功能,这表明可以通过扩展类似的机制来实现其他单目运算符[^3]。 但是需要注意的是,这些单目运算符的实现依赖于特定的语法约定(例如双击 `^` 表示平方),而通用的标准表达式形式。这意味着用户必须严格遵守预定义的输入格式才能获得预期的结果。 --- ### 改进建议与修复方案 为了使计算器能够正确处理带括号的复合表达式以及更广泛的单目运算符,建议采取如下措施: #### 解决 `2*(2+2)` 计算失败的方法 引入一个基于栈的数据结构用于解析评估含有多重嵌套的操作符及其对应的参数。这种方法被称为逆波兰表示法 (Reverse Polish Notation, RPN),或者称为后缀表达式算法。其实现步骤大致如下: 1. 将原始中缀表达式转化为后缀表达式。 2. 使用堆栈技术逐项读取后缀表达式的各个元素进行计算。 下面是改进后的伪代码框架: ```cpp #include <stack> #include <string> double evaluatePostfix(const std::string& postfixExpr) { std::stack<double> stack; for(char ch : postfixExpr){ if(isdigit(ch)){ stack.push(ch - '0'); // 假设只处理单个数字作为简化 } else{ double operand2 = stack.top(); stack.pop(); double operand1 = stack.top(); stack.pop(); switch(ch){ case '+': stack.push(operand1 + operand2); break; case '-': stack.push(operand1 - operand2); break; case '*': stack.push(operand1 * operand2); break; case '/': if(operand2 != 0) stack.push(operand1 / operand2); else throw "Division by zero!"; break; } } } return stack.top(); } ``` 对于实际应用而言,还需要编写额外的功能模块负责从中缀到后缀的转化过程。 --- #### 完善单目运算符的支持 针对现有的单目运算符实现不足之处,可通过增加更多内置函数进一步增强灵活性。例如,除了已经存在的平方外,还可以加入自然对数(`ln`)、常用对数(`log`)以及其他科学常量等功能。修改后的代码片段如下所示: ```cpp case 's': result = sin(num1); // 正弦函数 break; case 'c': result = cos(num1); // 余弦函数 break; case 't': result = tan(num1); // 正切函数 break; case 'l': result = log(num1); // 自然对数 ln() break; case 'L': result = log10(num1); // 常用对数 log10() break; case 'r': if(num1 >= 0) result = sqrt(num1); // 开根号 √x else throw "Square root of negative number!"; break; ``` 以上改动不仅提升了用户体验,还让整个应用程序更加贴近真实世界的场景需求。 --- ### 总结 综上所述,目前版本的C++计算器存在两方面的主要缺陷——缺乏对高级组合型表达式的适配能力以及有限度的单一目标指令集覆盖范围。通过对现有架构做出适当调整即可有效缓解这些问题带来的困扰。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值