当前位置:17727 > 17727.com > 面向对象的程序设计,关键字修饰函数的使用

面向对象的程序设计,关键字修饰函数的使用

文章作者:17727.com 上传时间:2019-12-28

读过一些关于关键字的介绍,但是在c++使用者还是不太熟练使用一些关键字声明函数;1.constconststringfun():表示返回的string不能被修改?stringfun()const:表示不能通过这个函数修改数据?statcivoidfun():不是太理解(好像也区分类外与内成员修饰?)externvoidfun():全局?我发现只要包含头文件就能被其他文件调用?inlinevoidfun():不太会调用,是否有相关链接或者大佬给我详细描述一下?谢谢

const修饰一个变量,代表这个变量是个常量,不可改变:

在C++的类定义里面,可以看到类似下面的定义:

理解对象

 const int n = 1; n++; /// error

class List {


1. 指向常量的指针
 const int * p1 = new int; *p1 = 3; /// error p1 = new int;/// works *p1 = 4; /// error

如上,p1是一个指针,将其看成是(const int) * p1,可知其指向的内容是一个常量,所以*p1 = 3会报错,但是,这个指针可以指向别的变量,不过其指向的内容依然不可变

private:

创建自定义对象的最简单方式就是创建一个Object的实例,然后再为它添加属性和方法

2. 指针本身是常量
 int * const p1 = new int++; /// works p1 = new int ; /// error

可以看成是int *,即p1是个常量,不可改变,但是p1指向的内容是可变的,所以++能够正常运行 自然,若是const int * const p1 = new int,则不管是指针本身,还是其指向的变量,都是不可变的。

  • void func(const int n):参数n是个常量,在函数里面不会被改变
  • void func(const int& n):按引用传值,且不会在函数体内被改变,比上一种方式好
  • void func(const int *p):指向常量的指针,其指向内容不会被改变
  • void func(int* const p):指针本身是常量,本身不会被改变

Node * p_head;

1     var person = new Object();
2     person.name = "Nicholas";
3     person.age = 29;
4     person.job = "SoftWare Engineer";
5     person.sayName = function(){
6         alert(this.name);
7     };
1.const修饰成员变量
class Foo{public: const int n; const int * p; Foo(int _n = 0, const int * _p = nullptr) : n, p{};};

const修饰成员变量,表示其为常量不可修改,注意其需在初始化列表里初始化

int length;

 

2.const修饰成员函数
class Foo{public: const int n; int m = 0; Foo(int _n = 0) : n{}; void func1()const { m++; /// error cout << n << endl; func2(); /// error } void func2() { m++; }};

const作用于成员函数的形式为:void func()const,其表示不会在函数体内修改任何成员变量,那它怎么保证我们无法修改成员变量呢?,基于下面的规则:

  • const成员函数内只能调用const成员变量:非const成员变量无法被调用,所以修改不了,const17727.com,成员变量能够被访问,但是其本身无法被修改
  • const成员函数不能调用非const成员函数:非const成员函数可能修改成员变量,所以禁止调用,而其他const函数因为不会改变成员变量,所以可以调用,“保证了纯正的血统” : P

……

创建对象的首选模式是对象字面量方式

3. const修饰对象
 const Foo foo; foo.m ++; /// error cout << foo.m << endl; /// works foo.func1(); /// works foo.func2(); /// error

const修改对象,表示这个对象是个常量,里面的数据不能被修改,其限制基于下面规则:

  • 公有变量只可读,不可写
  • 该对象只能调用const成员函数而const修饰对象指针也是同样的道理:
 const Foo * p1 = new Foo(); p1->m++; /// error cout << p1->m << endl; /// works p1->func1(); /// works p1->func2(); /// error

Public:

1     var person = {
2         name: "Nicholas",
3         age: 29,
4         job: "SoftWare Engineer",
5         sayName: function(){
6             alert(this.name);
7         }
8     };

int GetLength () const;
bool GetNodeInfo(const int index,Node & buffer) const {…… }

 

bool DeleteNode(const int index);

面向对象的程序设计,关键字修饰函数的使用。属性类型

…………

 

}
可以看到,在GetLength和GetNodeInfo两个成员函数的参数列表后面出现了一个const。这个const指明了这个函数不会修改该类的任何成员数据的值,称为常量成员函数。
对于const函数的外部定义,也不能忘记书写const限定符,如下面给出GetLength函数(指返回链表的长度)的定义:


int List::GetLength() const //这里依然不能忘记const

 

{
Return length;
}
如果在const成员函数的定义中出现了任何修改对象成员数据的现象,都会在编译时被检查出来,如:

内部属性无法直接访问,ECMAScript5把它们放在两对方括号中,分为数据属性和访问器属性

int List::GetLength() const

1、数据属性

{
Return length++; //错误!
}
const成员函数存在的意义在于它能被const常对象调用。我们都知道,在定义一个对象或者一个变量时,如果在类型前加一个const,如const int x;,则表示定义的量为一个常量,它的值不能被修改。但是创建的对象却可以调用成员函数,调用的成员函数很有可能改变对象的值,比如下面这段程序:

数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性

const List myList;

[[Configurable]]:表示能否通过delete输出属性从而定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为true

myList.DeleteNode(3); //错误,DeleteLength是非const成员函数
显然调用DeleteNode这个成员函数删除一个链表结点后,很有可能改变对象中length(链表长度)这个值,这不符合const对象的规定。但是,如果不允许const对象调用任何成员函数又是非常不合理的。于是,我们把那些肯定不会修改对象的各个属性值的成员函数加上const说明符,这样,在编译时,编译器将对这些const成员函数进行检查,如果确实没有修改对象值的行为,则检验通过。以后,如果一个const常对象调用这些const成员函数的时候,编译器将会允许。比如:

[[Enumerable]]:表示能否通过for-in循环返回属性,默认值为true

const List myList;

[[Writable]]:表示能否通过修改属性的值,默认值为true

myList.GetLength(); //正确,GetLength是const常函数,它返回链表长度,的确没有改变
3
//属性值的行为,被检验通过
你可能会问,为什么不在一个const常对象调用成员函数的时候再进行检查呢?如果被调用的函数会改变对象的属性值,则立即打住就是了。这样就不用麻烦地在成员函数后面加const限定符了。然而,这无疑会大大增加编译时间。考虑下面这段代码:

[[Value]]:包含这个属性的数据值,读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置

const List MyList;

 

MyList.GetLength();

要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()这个方法.这个方法接受三个参数:属性所在的对象、属性的名字和一个描述符对象

……

 

MyList.GetLength();

1     var person = {};
2     Object.defineProperty(person,"name",{
3         writable: false,//设置为不可修改属性的值
4         value: "Nicholas"
5     });
6 
7     console.log(person.name); // " Nicholas"
8     person.name = "Oreg";
9     console.log(person.name); // " Nicholas"

……
这段代码中,GetLength被调用了两次,但是编译时却也要检查两次,倘使一个成员函数被调用多次,那么他将在每次调用的时候都会被检查。这显然大大不利。而如果在定义类的时候加上const限定符对常函数加以标记,那么编译器只是检查一次就好,在const对象调用成员函数时,const函数将会被直接放行。所以,C++采取了const限定符描述常函数方案而摈弃了后者。所以,即使一个函数没有修改对象值的行为,如果没有加上const限定符说明是常函数,那么const对象依然不能调用它。
然而,有些时候,我们却必须要让const函数具有修改某个成员数据值的能力。比如一些内部的状态量,对外部用户无所谓,但是对整个对象的运行却大有用处,如支持缓存的技术。遇到这种问题,我们可以把一个成员数据定义为mutable(多变的),它表示这个成员变量可以被const成员函数修改却不违法。比如下面定义了一个is_valid类成员:

 

class List

 

{

把donfigurable设置为false,表示不能从对象中删除属性。一旦把属性定义为不可配置的,就不能再把它变回可配置的了。此时,在调用Object.defineProperty()方法修改处writable之外的特性,都会导致错误

private:

 1     var person = {};
 2     Object.defineProperty(person,"name",{
 3         configurable: false,
 4         value: "Nicholas"
 5     });
 6 
 7     //抛出错误
 8     Object.defineProperty(person,"name",{
 9         configurable: true,
10         value:"Nicholas"
11     });

……

 

mutable bool is_valid;

在调用Object.defineProperty()方法时,如果不指定,configurable、enumerable和writable特性的默认值都是false

……

 

public:

2、访问器属性

bool CheckList() const

访问器属性不包含属性值;他们包含一对儿getter和setter函数,在读取访问器属性时,会调用setter函数,这个函数负责返回有效的值,在写入访问器属性时,会调用getter函数,并传入新值,这个函数决定如何处理数据。访问器属性有如下4个特性

{

[[Configurable]]:表示能否通过delete输出属性从而定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为true

if(length >= 1) then return is_valid =true;

[[Enumerable]]:表示能否通过for-in循环返回属性,默认值为true

else return is_valid = false; //正确!

[[Get]]:在读取属性时调用的函数。默认值为undefined

};

[[Set]]:在写入属性时调用的函数。默认值为undefined

这样,即使像CheckList这样的const成员函数修改它也是合法的。
但需要注意的时,不可滥用mutabe描述符,如果在某个类中只有少数一部分是被允许const常量函数修改的,使用mutable是再合适不过的。如果大部分数据都定义为mutable,那么最好将这些需要修改的数据放入另一个独立的对象里,并间接地访问它。

 

参考资料:《C++ Programming Language 第三版》
注:本文属知无涯原创,转载请注明出处!

访问器属性不能直接定义,必须使用Object.defineProperty()来定义。

 1     var book = {
 2         _year: 2004,
 3         edition: 1
 4     };
 5 
 6     Object.defineProperty(book,"year",{
 7         get: function(){
 8             return this._year;
 9         },
10         set: function(newValue){
11             if(newValue > 2004){
12                 this._year = newValue;
13                 this.edition += newValue - 2004;
14             } 
15         }
16     });
17 
18     book.year = 2005;
19     console.log(book.edition); //2

 

 

这是使用访问器的常见方式,即设置一个属性的值会导致其他属性发生变化,year前面的下划线是一种

常用的记号,用于表示只能通过对象方法访问的属性

不一定非要同时指定getter和setter。只指定getter意味着属性是不能写的,只指定setter函数的属性
也不能读

两个非标准的方法:__defineGetter__()和__defineSetter__()

 1     var book = {
 2         _year: 2004,
 3         edition: 1
 4     };
 5 
 6     book.__defineGetter__("year",function(){
 7             return this._year;
 8         });
 9     book.__defineSetter__("yeat",function(newValue){
10                     if(newValue > 2004){
11                 this._year = newValue;
12                 this.edition += newValue - 2004;
13             } 
14     });
15 
16     book.year = 2006;
17     console.log(book.year);

 

定义多个属性


 

Object.defineProperties()方法。利用这个方法可以通过描述符一次定义多个属性。

 1     var book = {};
 2 
 3     Object.defineProperties(book,{
 4         _year: {
 5             value: 2004
 6         },
 7         edition: {
 8             value: 1
 9         },
10         year: {
11             get: function(){
12                 return this._year;
13             },
14             set: function(newValue){
15                 if(newValue > 2004){
16                     this._year = newValue;
17                     this.edition += newValue - 2004;
18                 }
19             }
20         }
21     });

 

读取属性的特性


Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符

 

 1     Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符
 2 
 3     var book = {};
 4 
 5     Object.defineProperties(book,{
 6         _year: {
 7             value: 2004
 8         },
 9         edition: {
10             value: 1
11         },
12         year: {
13             get: function(){
14                 return this._year;
15             },
16             set: function(newValue){
17                 if(newValue > 2004){
18                     this._year = newValue;
19                     this.edition += newValue - 2004;
20                 }
21             }
22         }
23     });
24 
25     var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
26     console.log(descriptor.value); //2004
27     console.log(descriptor.configurable); //false
28     console.log(typeof descriptor.get); //"underfined"
29 
30     var descriptor = Object.getOwnPropertyDescriptor(book,"year");
31     console.log(descriptor.value); //underfined
32     console.log(descriptor.enumerable); //false
33     console.log(typeof descriptor.get); //"function"

 

本文由17727发布于17727.com,转载请注明出处:面向对象的程序设计,关键字修饰函数的使用

关键词: