关于const用法的总结

#include <iostream>

#if 0               //测试const 变量
int main()
{

    int k = 0;
    const int i = 50;
    //const int j;   //  const 变量定义时候必须初始化,const类成员变量
                      //必须在初始化列表中初始化,因为初始化列表发生在对象构造前,const变量一单对象构建完毕
                      //再操作它,就是赋值,而赋值是不被允许的.....
    const int jj = 30;
    //int&cc;        //  引用也是定义时候必须初始化
    int &cc = k;
    //i = k;          //const 变量不能再被赋值了
    k = i;
}
#endif


#if 0   //测试指针常量   const int *a;二级指针修改  一级指针的值
void Test(const int *a)
{
    //*a = 1;//不允许改变,因为是指针常量,指针指向的为一个常量
    a = new int(10086);//这个指针a,分配在栈区,出了作用域,就销毁掉了,要想改变一级指针,需要二级指针才行
                        //如下:
}
void Test02(int **a)   //二级指针,把实参的地址,扔过来.... void Test02(const int **a)错误,
{
    *a = new int(10076);
}

int main()
{
    int *a = new int(10000);
    printf("%d", *a);
    Test(a);
    printf("%d", *a);
    Test02(&a);
    printf("%d", *a);
    getchar();
    return 0;
}

#endif


//测试const引用,一般用引用就是想改变传递过来的值,如果不想改变传递过来的值,
//直接传值就好了(具体为何,自己脑补:传值传递的是一份copy,对copy的操作,跟实参没有半毛线关系)
//引用主要用在  自定义数据类型,做函数参数,值传递影响效率,而引用传递又担心更改实参的值
//,故:class A; A const &;,解决了效率,又限制了修改 如:将函数 void Test(A a) 改为-> void Test(const A &a)
#if 0
void Test(const int &a) //若形参为const A& a,则不能改变函数传递进来的引用对象,从而保护了原对象的属性。

                  //对于自定义的数据类型,用引用传递速度较快,如果不想改变原值,就可以用const来保护参数
{
    //a = 2;           //const 引用,同const指针
}
int main()
{
    int a = 3;
    Test(a);
    reurn 0;
}

#endif

#if 0
//const 用于修饰函数返回值
//用法1:用const 修饰返回值为对象本身(非引用,非指针),多用于二目操作符重载函数 并 产生新对象的时候
//不建议const Ratioan ,以上用法,也建议返回 const Ratioan & ,因为不仅不支持链式操作 ,而且,生产出来的是
//const 对象,const 对象,不允许赋值,只能访问类中的const成员函数和公有成员:::这种情况很少用到

class Rational
{
public:
    Rational(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    //重载二目运算符,做成员函数,只需要一个参数,因为还有this指针作祟....
    friend const Rational operator* (const Rational& lhs, const Rational& rhs)  //
    {
        return Rational(lhs.a * rhs.a, lhs.b * rhs.b);
    }

    friend const Rational & operator+ (const Rational& lhs, const Rational& rhs)  //
    {
        return Rational(lhs.a + rhs.a, lhs.b + rhs.b);
    }

    void Sety(int a,int b)
    {
        this->a = a;
        this->b = b;
    }

public:
    int a;
    int b;
};

int main()
{
    Rational ra1(2,4);
    Rational ra2(3,6);
    Rational ra = ra1 + ra2;   //重载 +,为友元函数
    Rational ra3(1, 3);
    //此时
    //(ra1 +ra2) = ra3 ;因为是返回值const Rational 所以,不能赋值
    Rational ra4 = ra1*ra2;
    //ra.Sety();    //ra是const 对象,只能访问const 成员函数

    //ra4.Sety();      //ra4是const 对象,只能访问const 成员函数

    getchar();
    return 0;
}

#endif

#if 0   //const 指针只能赋值 给 const 指针,反过来,cosnt指针,可以接受非const 指针
const char *GetString(void)
{

}
int main()
{
    //char *str1 = GetString();  //指向为const的指针 只能赋值给const 指针
                                 //因为 指向为const的指针,要求,指向的内容不能改变,赋给非cosnt指针,无法保证这一点
    const char *str2 = GetString();
    return 0;
}

#endif

//函数返回值采用"引用传递"的不多,这种方式一般就出现的类的  赋值函数 中,目的是为了实现链式表达

#if 1
class A
{
public:
    A(int a, int b)
    {
        this->a = a;
        this->b = b;
    }

    A & operator = (const A &other)
    {
        return A(this->a = other.a, this->b = other.b);
    }

public:
    int a;
    int b;
};

int main()
{
    A a(0,0);
    A b(1,1);
    A c(3,2);
    A d(4, 4);
     a = (b = c)= d;
    (a = b) = c = d;   //(a =b)返回一个引用,不是不能被赋值吗?   这是第一次,初始化,每一次都是第一次,哈哈
    return 0;
}
#endif


//4.在类成员函数的函数体后加关键字const
//
//在类成员函数的函数体后加关键字const,形如:void fun() const;
//在函数过程中不会修改数据成员。如果在编写const成员函数时,不慎修改了数据成员,
//或者调用了其他非const成员函数,编译器将报错,这大大提高了程序的健壮性。
//如果不是在类的成员函数,没有任何效果,void fun() const; 和void func(); 是一样的。


//5.在另一连接文件文件中引用常量
//
//方法:在类型前添加关键字extern const,表示引用的是常量,因此该常量不允许被再次赋值,举例如下:
//
//extern const int i;       // 正确
//extern const int j = 10;  // 错误,常量不可以被再次赋值


//const常量 和 #define宏
//宏只是进行简单的字符替换,没有类型检查,并且在字符替换时,可能产生各种意料之外的错误

//#define I 10;
//char h = I;    //数据截断了也不会报错
但是
//const  long &i = 20;     //  这种写法也可以....
//char cc = i;//编译警告;可能由于数的截断带来错误赋值

//
//2.使用const可以避免不必要的内存分配
//
//从汇编的角度来看,const定义常量只是给出了对应的内存地址, 而不是象#define一样给出的是立即数
//,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。例子如下:
//
//#define k "Hello world!"
//const char pk[] = "Hello world!";
//printf(k);      // 为k分配了第一次内存
//printf(pk);     // 为pk一次分配了内存,以后不再分配
//printf(k);      // 为k分配了第二次内存
//printf(pk);     //不再分配内存了

 

#if 0
#include <iostream>
using namespace std;

int main(int argc, char const* argv[])
{
    const int& a = 1;           //字面量的引用  ,必须是const 引用
    cout << &a << endl;

    const int i = 11;
    //int *p1 = &i;      //这种写法错误,const变量只能赋值 指向const 引用/指针
    const int *p2 = &i;  //这个就const  引用

    int *p = (int *)&i;   //c语言的暴力无下限转化法则,强制去掉了 i的const 属性
    *p = 100;
    cout << *p << "改变了吧";
    getchar();    
    return 0;
}
#endif


//2.构造函数不能被声明为const
//
//3.const数据成员的初始化只能在类的构造函数的初始化表中进行

//class A
//{
//public:
//    const int a;
//    int &c;
//    A(int x,int cc) :a(x),c(cc) //正确,const成员函数和引用类型要在初始化列表进行初始化
//    {                                   //实现体内的豆角 赋值了
//        //a = x;            //错误
//        
//    }
//};

 

//4.在参数中使用const应该使用引用或指针,而不是一般的对象实例
//
//合理利用const在成员函数中的三种用法(参数、返回值、函数),一般来说,不要轻易的将函数的返回值类型定为const;
//另外,除了重载操作符外一般不要将返回值类型定为对某个对象的const引用。

//class Stu
//{
//public:
//    const Stu& findxx(const Stu& a, const Stu& c) const
//    {
//        //一般很少将一个函数的返回值设置为某个对象的 const 引用
//    }
//    
//protected:
//private:
//};


#if 0
class A
{
public:
    int x;
    void set(int x)
    {
        this->x = x;
    }
    const A& Test1()const       //成员函数如果有返回值,尽量不要设置为const成员函数
                                //因为const成员函数,返回值也是const类型
    {
        //x = 2;    //const成员函数,承诺不修改任何数据
        return *this;  // 不限于*this。不管返回的是什么,哪怕是一个定义为非const的对象,结果也是一样的
    }

    /*A& Test3()const     //会报错,因为返回的是一个const对象
    {
        return *this;
    }*/

    A& Test2(const int a,const int b)   //替代方法:利用把参数设置为 const类型,替代const成员函数
    {

    }
};


int main()
{
    A a, b;
    b = a.Test1();   //a.Test1(),返回的是一个const 对象,赋值:是值copy,跟产出的const 对象没有半毛钱关系

    //A c;
    //b = c;           // correct b是a.Test1()返回的一份内存的copy,所有对b重新赋值,跟a.Test1()的const返回值,没有半毛钱关系

    //引用和指针就不一样了,都是地址,是同一个变量
    //const A &f = a.Test1(); //correct
    //                        //,既然是别名,那么别名的类型要与原来的类型相同
    //                        //a.Test1(),返回一个const 对象 ,const 对象的地址 只能由const引用/指针来接
    //                       //蛋疼的是引用定义的时候必须直接初始化...所以只能这样写

    a.set(2); //corerct 因为,这里已经出了 Test1()的作用域了
    b.set(33);  //correct ,因为b 跟a.Test1()的const返回值没有半毛钱关系,,,,因为不是指针也不是引用,不是指针不是引用
                //就是值copy()

    //a.Test1().set(2);  //必须报错,因为a.Test1()返回的const 对象,只能调用  const成员函数
    a.Test1().Test1();   //you look ,调用const成员函数就ok    
}
#endif

#if 0
//mutable 破除const不能修改的魔咒
class A
{
public:
    int x;
    mutable int y;                  //破除魔咒
    A(int a, int b) :x(a), y(b)
    {

    }
};

int  main()
{
    const A a(2, 3);
    //a.x = 1;     //因为const对象:会把自身的数据成员全部默认加上const属性.....是不能修改其内的数据成员的
    a.y = 33;        //加了mutable关键字,就可以摸出魔咒
    return 0;
}

#endif

参考链接:

https://blog.youkuaiyun.com/liyuefeilong/article/details/48900121##1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值