谈谈int*转const int* &的问题

最近看代码遇到个指针的const引用的问题,指针真的是让人又爱又恨呐,太微妙了。指针和引用的再深入,记录分享下

情况是这样的

我是在这里遇到的,简化代码如下:

template <class T>
void show1(const T & i)
{
    // i = (T)0x555555;   编译报错
    // *i = 1314;   编译通过,效果正常
    cout << (void*)i << endl;
}
void show2(const int* & i)
{
    cout << (void*)i << endl;
}
int nTemp = 2019;
int * ptrInt = &nTemp;
show1(ptrInt);           //可以通过编译,而且我已验证确实是真正的const引用
show2(ptrInt);       //不能通过编译。cannot bind non-const lvalue reference of type 'const int*&' to an rvalue of type 'const int*'
/

简单来说就是这么个情况:

int nTemp = 2019;
int *ptrInt = &nTemp;
const *ptrInt2 = &nTemp;
const int* &refptrInt1 = ptrInt;   //1.这种用法会报错。你思考过吗,你觉得呢?

//下面是验证代码
const int* &refptrInt2 = ptrInt2;   //2.这个引用是const的吗
// *refptrInt2 = 999;      //error: assignment of read-only location '* refptrInt2'
refptrInt2 = (int*)0x1234;

int* const &refptrInt2 = ptrInt;    //3.其实,这个才是ptrInt的真正const引用。
*refptrInt2 = 999;
// refptrInt2 = (int*)0x1234;    //error: assignment of read-only reference 'refptrInt2' 

阐述一下

谈谈我的理解吧!
问题的关键还是在于const限定符修饰指针类型数据的神奇之处。也就是const*之前之后的差异。

像如下这种单个token的类型说明符(比如int)很好理解,

int a = 0;
const int &crefa = a;(等同表示:int const &crefa = a;)  

crefa标识符为const int引用类型,不能修改其标识符对应存储的值。

而像对于指针这种需要两个token的类型说明符(int*)的情况,就有时让人绕不过弯了。

int *pa = &a;

(int*)类型pa标识符对应的const引用应该是哪种呢?首先,其const引用,表示的效果应该也是其标识符对应存储的值不能修改。指针还不就是一个存储的值为地址的特殊变量。然后,了解了const在*前后的差异就该明白了。

int nage = 18;
const int *pt = &nage;    //a pointer to const int,不能修改指针存储的地址指向的变量的值。 解引用后内容是常量。
int* const pu = &nage;    //a const pointer to int,不能修改指针存储的地址,即改变不了指向的位置了。指针变量内容是常量。

其对应的const引用,应该这样表达:

int* const &crefpa = pa;   //和验证代码里的是一致的,这才是真正的指针的const引用想表达的意思

而不该是const int* &crefpa = pa; 其对应的该是const int* pa = &a;

而且,引用设计上还不就是指针,其实就是类似如下的伪装表示:

int a;
int & refint = a;   
int * const pint = &a;

其const就该是

int [const] &refint = a;  
int [const] * const pint = &a;  
//[const] int * const pint = &a; 同上

当为单个token,int时,其和const交换不影响编译器语义,如果是双token,int*时,不影响才怪呢。

int* [const] &refint = a;  
int* [const] * const pint = &a;  
//[const] int* * const pint = &a;  还同上吗?

顺便说下,我遇到的情况,更验证了我的说辞。
而且,模板函数实例化看来是做了特殊处理了。类型参数T,可不是简单的替换啊,是一个独立的部分了。 比如当T == int *时,const T & 和T const&,是完全一样的,不影响语义,都是int* const &可以放心用其表示const引用。

template < class T >
void show1(const T & i)
{
// i = (T)0x555555; 编译报错
// *i = 1314; 编译通过,效果正常
cout << (void*)i << endl;
}


myAvatar

### C++ 中运算符重载的用法和实现 #### 一、运算符重载的概念 运算符重载本质上是对现有运算符赋予新的含义,使其能够作用于用户自定义的数据类型。通过这种方式,C++ 提供了一种机制来扩展语言的功能,使得程序设计者可以用更自然的方式表达复杂的逻辑[^1]。 #### 二、运算符重载的语法格式 在 C++ 中,可以通过两种方式实现运算符重载:作为类的成员函数或者全局函数。无论哪种形式,都需要遵循特定的语法规则: - **成员函数**:如果将运算符重载为类的成员函数,则第一个操作数隐含地绑定到 `this` 指针上。 - **全局函数**:当需要处理不同类型的对象时,通常会将其声明为友元函数或普通的全局函数。 以下是通用的语法结构: ```cpp // 成员函数的形式 ReturnType ClassName::operator OperatorSymbol(ArgumentList); // 全局函数的形式 friend ReturnType operator OperatorSymbol(const ArgumentType&amp; lhs, const ArgumentType&amp; rhs); ``` #### 三、常见的运算符重载实例 ##### 1. 加号 (`+`) 和减号 (`-`) 的重载 加号和减号是最常用的算术运算符之一,它们可以被用来连接字符串、组合向量或其他复杂数据类型。下面是一个简单的例子展示如何重载加号用于两个自定义类的对象相加: ```cpp class Complex { public: double real; double imag; // 构造函数初始化列表简化代码 Complex(double r = 0, double i = 0) : real(r), imag(i) {} // 成员函数版本 Complex operator+(const Complex&amp; other) const { return Complex(real + other.real, imag + other.imag); } }; int main() { Complex c1(3, 4), c2(5, -6); Complex result = c1 + c2; // 调用了Complex::operator+ std::cout &lt;&lt; &quot;Result: (&quot; &lt;&lt; result.real &lt;&lt; &quot;, &quot; &lt;&lt; result.imag &lt;&lt; &quot;)&quot; &lt;&lt; std::endl; } ``` 对于不同类型的操作数之间的加法(混合模式),推荐使用全局函数实现[^2]。 ##### 2. 流插入器 (`&lt;&lt;`) 和提取器 (`&gt;&gt;`) 流插入器和提取器允许我们轻松地打印和解析对象的内容至标准输入输出设备。这通常是通过定义这些运算符为类的朋友函数完成的: ```cpp #include &lt;iostream&gt; using namespace std; class Point { private: int x, y; public: Point(int a=0, int b=0):x(a),y(b){} friend ostream&amp; operator&lt;&lt;(ostream&amp;, const Point&amp;); friend istream&amp; operator&gt;&gt;(istream&amp;, Point&amp;); }; ostream&amp; operator&lt;&lt;(ostream&amp; os, const Point&amp; p){ os &lt;&lt; &quot;(&quot; &lt;&lt; p.x &lt;&lt; &quot;,&quot; &lt;&lt; p.y &lt;&lt; &quot;)&quot;; return os; } istream&amp; operator&gt;&gt;(istream&amp; is, Point&amp; p){ cout &lt;&lt; &quot;Enter X and Y coordinates:&quot; ; is &gt;&gt; p.x &gt;&gt; p.y; return is; } int main(){ Point pt; cin &gt;&gt; pt; cout &lt;&lt; &quot;Point entered is: &quot; &lt;&lt; pt &lt;&lt; endl; } ``` ##### 3. 自增 (++) 和自减 (--)[^2] 前置与后置增量/ decrement 运算符的行为略有差异&mdash;&mdash;前者立即修改变量并返回新值;后者先保存当前状态再执行更新动作。因此,在编写此类运算符时需要注意区分这两种情况: ```cpp class Counter{ private: unsigned count_; public: explicit Counter(unsigned init_val = 0) : count_(init_val){} // 前置++ Counter&amp; operator++(){ ++count_; return *this;} // 后置++, 需要额外参数表示这是后缀调用 Counter operator++(int){ Counter old_state(*this); ++(*this); return old_state; } }; ``` #### 四、注意事项 尽管运算符重载提供了极大的灵活性,但也存在一些潜在的风险和限制条件。例如,并非所有的内置运算符都可以被重新定义;某些情况下过度依赖它可能导致难以理解甚至错误的结果。所以应该谨慎选择哪些地方适合应用这一特性[^2]。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值