Const的几大用法(常用)_const值消耗大

例如:

void main(void)
{
    const int i = 10;    //i,j都用作常变量
    int const j = 20;
    i = 15;            //错误,常变量不能改变
    j = 25;            //错误,常变量不能改变
}

第二:常指针

const跟指针一起使用的时候有两种方法。

const可用来限制指针不可变。也就是说指针指向的内存地址不可变,但可以随意改变该地址指向的内存的内容。

int main(void) 
{ 
 int i = 10;
 int *const j = &i;  //常指针, 指向int型变量
 (*j)++;     //可以改变变量的内容
 j++;      //错误,不能改变常指针指向的内存地址
}

const也可用来限制指针指向的内存不可变,但指针指向的内存地址可变。

int main(void)
{
 int i = 20;
 const int *j = &i;  //指针,指向int型常量
 //也可以写成int const *j = &i;
 j++;    //指针指向的内存地址可变
 (*j)++;   //错误,不能改变内存内容
}

看完上面的两个例子,是不是糊涂了?告诉你一个诀窍,

在第一个例子中,const用来修饰指针j,j不可变(也就是指向int变量的常指针);

第二个例子中,const用来修饰*j,*j不可变(也就是指向int常量的指针)。

这两种方式可以组合起来使用,使指针和内存内容都不可变。

int main(void)
{
 int i = 10;
 const int *const j = &i; //指向int常量的常指针
 j++;       //错误,不能改变指针指向的地址
 (*j)++;    //错误,不能改变常量的值
}

第三:Const和引用

引用实际上就是变量的别名,这里有几条规则:

声明变量时必须初始化

一经初始化,引用不能在指向其它变量。

任何对引用的改变都将改变原变量。

引用和变量本身指向同一内存地址。

下面的例子演示了以上的规则:

void main(void)
{
    int i = 10;                    //i和j是int型变量
    int j = 20;        
    int &r = i;                    //r 是变量i的引用
    int &s;                        //错误,声明引用时必须初始化
    i = 15;                        //i 和 r 都等于15
    i++;                        //i 和 r都等于16
    r = 18;                        //i 和r 都等于18
    printf("Address of i=%u, Address of r=%u",&i,&r);    //内存地址相同
    r = j;                        //i 和 r都等于20,但r不是j的引用
    r++;                        //i 和 r 都等于21, j 仍等于20
}

用const修饰引用,使应用不可修改,但这并不耽误引用反映任何对变量的修改。Const加在数据类型前后均可。

例如:

void main(void)
{
    int i = 10;
    int j = 100;
    const int &r = i;
    int const &s = j;
    r = 20;          //错,不能改变内容
    s = 50;          //错,不能改变内容
    i = 15;          // i和r 都等于15
    j = 25;          // j和s 都等于25
}

第四:Const和成员函数

声明成员函数时,末尾加const修饰,表示在成员函数内不得改变该对象的任何数据。这种模式常被用来表示对象数据只读的访问模式。例如:

class MyClass
{
    char *str ="Hello, World";
    MyClass()
    {
        //void constructor
    } 
    ~MyClass()
    {
//destructor 
    }
 
    char ValueAt(int pos) const    //const method is an accessor method
    {
        if(pos >= 12)
               return 0;
  *str = 'M';       //错误,不得修改该对象
        return str[pos];     //return the value at position pos
    }
}

第五:Const和重载

重载函数的时候也可以使用const,考虑下面的代码:

<think> 我们正在解释代码语句:`const CCfgCustOutVehDynamics& f_cfgCustOutVehDynamics_r` 根据用户提供的引用和之前的回答,我们可以进一步补充和明确解释。 ### 1. 语法解析 - **`const`**:这是一个类型限定符,表示该对象是常量,即不可修改。在函数内部,任何试图修改该对象的操作都会导致编译错误[^3]。 - **`CCfgCustOutVehDynamics`**:这是一个自定义类型(类或结构体),根据命名推测,它表示车辆动力学的配置信息(Configuration for Custom Output Vehicle Dynamics)。此类可能包含车辆动力学相关的参数,如最速度、加速度、转向特性等。 - **`&`**:表示这是一个引用(reference),而不是传递。引用是原对象的别名,传递引用时不会发生对象拷贝,只传递地址。 - **参数名 `f_cfgCustOutVehDynamics_r`**:这里的命名习惯带有后缀`_r`,这是一种常见的编程习惯,用于明确指示该变量是一个引用(reference)。前缀`f_`可能表示函数参数(function parameter)。 ### 2. 核心用途 这种参数传递方式(常量引用)的主要目的是: 1. **避免拷贝开销**:如果`CCfgCustOutVehDynamics`是一个包含量数据的对象(例如,包含多个传感器配置、车辆模型参数等),直接传递会导致整个对象被复制,消耗量时间和内存。引用传递仅传递对象的地址(通常4或8字节),避免了拷贝[^4]。 2. **保证数据安全性**:`const`限定符确保函数内部无法修改传入的对象,保护原始数据不被意外更改。这对于配置信息尤其重要,因为配置通常是只读的[^3]。 3. **支持多态**:如果`CCfgCustOutVehDynamics`是一个基类,实际传入的可能是一个派生类对象。通过引用传递,可以安全地调用虚函数实现多态(但需要注意对象切片问题在引用传递中不会发生)[^1]。 ### 3. 与指针传递的对比 引用传递与指针传递在性能上几乎相同(都是传递地址),但引用传递有以下优势: - **无需空指针检查**:引用必须绑定到有效对象(不能为NULL),因此无需在函数内检查空指针。 - **语法简洁**:使用引用时可以直接像使用对象一样操作(例如`obj.method()`),而指针需要解引用(`ptr->method()`)。 ### 4. 使用示例 ```cpp // 假设CCfgCustOutVehDynamics有一个成员函数GetMaxSpeed() void ProcessVehicleConfig(const CCfgCustOutVehDynamics& cfg_r) { // 合法操作:读取配置 float maxSpeed = cfg_r.GetMaxSpeed(); std::cout << "Max speed: " << maxSpeed << std::endl; // 非法操作:尝试修改cfg_r(编译错误) // cfg_r.SetMaxSpeed(200); } int main() { CCfgCustOutVehDynamics vehConfig; // 创建配置对象 ProcessVehicleConfig(vehConfig); // 通过常量引用传递 return 0; } ``` ### 5. 相关引用内容补充 - 引用[2]提到在消息传递中,使用`const`指针可以避免复制,尤其在处理型消息(如点云数据)时性能提升显著。同理,此处使用常量引用也是为了避免复制型配置对象[^2]。 - 引用[3]明确区分了`const string parameter`(传递的常量副本)和`const string& parameter`(常量引用),后者既避免拷贝又保证原对象不被修改[^3]。 ### 6. 性能优化延伸 在C++11及以上版本中,如果函数需要修改传入的对象,但调用方不关心原对象的状态,可以使用**移动语义**(通过右引用传递)。例如: ```cpp void ModifyConfig(CCfgCustOutVehDynamics&& cfg_moved) { cfg_moved.SetParam(...); // 可修改 // 转移资源所有权,避免深拷贝 } ``` §§相关问题§§ 1. 常量引用传递与`const`指针在性能上有何差异? 2. 如果需要在函数内修改配置对象,应如何调整参数声明? 3. 什么场景下必须使用传递而非引用传递? 4. C++11的移动语义如何优化型对象的传递效率? </think> ### 解释代码语句:`const CCfgCustOutVehDynamics& f_cfgCustOutVehDynamics_r` #### 1. **语法结构分解** | 组成部分 | 说明 | |------------------------------|----------------------------------------------------------------------| | `const` | 类型限定符,声明对象为**只读**,禁止任何修改操作[^3] | | `CCfgCustOutVehDynamics` | 自定义类型(类/结构体),推测为车辆动力学输出配置 | | `&` | 引用符号,表示参数以**引用方式传递**(非拷贝) | | `f_cfgCustOutVehDynamics_r` | 参数命名:`f_`前缀(函数参数),`_r`后缀(引用标识) | #### 2. **核心设计目的** 1. **零拷贝高效传递** - 引用传递避免对象复制(尤其当`CCfgCustOutVehDynamics`含量数据时) - 传递效率:仅复制**地址**($4-8$字节),对比传递需完整对象复制[^4] 2. **数据安全保证** - `const`确保函数内部**无法修改**原始配置 - 防止意外篡改关键参数(符合配置数据的只读特性)[^3] 3. **资源优化** - 避免智能指针(如`boost::shared_ptr`)的开销 - 直接访问原对象,无引用计数管理负担[^2] #### 3. **应用场景与示例** ```cpp // 配置处理函数(典型用法) void ProcessVehicleProfile(const CCfgCustOutVehDynamics& cfg_r) { // ✅ 安全读取配置(无拷贝) double max_torque = cfg_r.getMaxTorque(); // ❌ 禁止修改配置(编译错误) // cfg_r.setMaxSpeed(120); } // 调用端 CCfgCustOutVehDynamics mainConfig; ProcessVehicleProfile(mainConfig); // 隐式引用传递 ``` #### 4. **与替代方案的性能对比** | 传递方式 | 内存开销 | 安全性 | 适用场景 | |------------------------|------------------|--------------|-----------------------| | **常量引用** | $O(1)$(地址) | 高(只读) | 型只读对象[^4] | | 传递 | $O(n)$(全拷贝) | 中(副本) | <10KB小型对象 | | `const`指针 | $O(1)$ | 中(需判空) | 需显式空处理场景[^2]| | `shared_ptr<const T>` | $O(1)+$引用计数 | 高 | 需所有权共享的场景 | > 💡 **最佳实践**:当对象小 $size(T) > 2 \times sizeof(void\*)$ 时,优先使用常量引用传递 #### 5. **在回调机制中的特殊价** - 适用于ROS/C++消息回调等场景 - 避免`sensor_msgs::PointCloud2`等型消息的复制开销[^2] - 对比指针传递更安全(无空指针风险)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值