C++与C#对照学习:让对象变得像常量
我们知道如果不希望改变某个变量的值就用const修饰它,让它变为常量.基本类型和指针类型都能用const来修饰.数组也可以用const来修饰.
那你自然要问我们把class实例化成一个对象后,如果不希望修改对象中的成员变量,让对象变得像个常量一样该咋整呢?
你首先自然会想到把类中所有的成员变量都用const修饰不就得了.但在C++中成员变量不能声明成const,因为声明成const时要同时赋值,但成员变量是不能直接赋值的,必须通过构造函数或其他函数.所以除非你声明成static const.但显然我们不是需要一个静态变量.
其实很简单,我们只要实例化类时加个const修饰下就OK,但是由于类不是个简单的类型.所以还涉及到一些特殊的操作.还是举个简单的例子看下.
class MyClass
{
public:
int age;
string name;
MyClass(int num,string str)
{
age = num;
name = str;
}
void Test() { Print(); }
void Print() const
{
std::cout<<name<<endl;
}
~MyClass(void);
};
const MyClass my(110,"arwen"); //实例化类时用const修饰.
my.age = 911; //这样会出错,既然用const修饰了肯定就变得像常量一样,不能改变成员变量的值了.
my.Print(); //正确执行
my.Test(); //出错了
为啥调用my.Test()时会出错呢? 这就是特殊的地方了.当用const修饰一个对象后,此对象就只能调用const修饰的函数了(const必须放到函数变后面).而一般的函数是不能被调用了.
为啥这样其实很简单,出于一致性考虑.既然不让直接修改成员变量了.那任何潜在的有可能修改成员变量的操作都就禁止.因为一般函数中有可能会修改了成员变量值.所以干脆禁止调用.
函数后面加个const是代表啥意思呢.它是代表在此函数中不能修改成员变量的值.例如
void Print() const { age = 456;} //这样会出错.当然你如果在函数中修改其他不是成员变量的值就没问题.
const修饰的函数中如果有调用到其他函数,那被调用的函数也只能是后面被const修饰的.当然一般函数调用const修饰的函数是没啥限制的.
所以通过当用const修饰对象后
1.不能像my.age = 911这样直修改成员变量
2.对象只能调用被const修饰的函数
3.被const修饰的函数中不能修改成员变量,且它也只能调用其他也被const修饰的函数.
通过这三点确保了成员函数在任何情况下都不会被修改.用构造函数赋值后就一直不变.
上面说到的用const修饰函数也可以单独用,不一定和const修饰对象配合用.单独用的话约定也是一样,函数中不能修改成员变量
上面说的是在C++中,那在C#中呢?
首先C#中一般把成员变量叫作字段.跟C++不同,C#中可以直接给字段赋值的.所以用const修饰类中的字段,然后赋值是没问题的.但显然我们很多时候是希望用构造函数来赋值的.那咋整呢?也像C++一样用个const修饰下就OK了吗.那不行.因为用const修饰引用类型时只能给赋值为null,那等于说那对象没任何用处了.当然C#自然有其他操作可以达到类似的效果.那就是通过属性加权限修饰符.
class MyClass
{
private int age;
private string name;
public int Age {
get{return age;}
}
public int Name {
get{return name;}
}
MyClass(int num,string str)
{
age = num;
name = str;
}
}
如果不希望用户修改字段.就把字段全部设为private的.然后用属性提供个接口.只有读的权限,不能修改.只有构造函数能赋值一次.那你可能会问其他成员函数会修改啊?
这就需要遵守一个约定了,就是类中其他函数别直接去用字段,也只是去用属性Age,Name.当然这只是推荐的作法,如果你实在想去直接用字段,去修改了.那就没办法了啊.
反正C#中使用了属性,推荐的作法就是在成员函数中直接用属性,别直接用字段了.
另外还有个办法就是用readonly修饰字段,这样就只能读不能修改了.