zoukankan      html  css  js  c++  java
  • java和C++的const 和 final 的区别

    In C++ marking a member function const means it may be called on const instances. Java does not have an equivalent to this

    把一个类到成员函数标记为const表示它只能被const的实例调用

    class Foo {
    public:
       void bar();
       void foo() const;
    };
    
    void test(const Foo& i) {
       i.foo(); //fine
       i.bar(); //error
    }

    In Java the final keyword can be used for four things:

    • on a class or method to seal it (no subclasses / overriding allowed)
    • on a member variable to declare that is it can be set exactly once (I think this is what you are talking about)
    • on a variable declared in a method, to make sure that it can be set exactly once
    • on a method parameter, to declare that it cannot be modified within the method

    One important thing is: A Java final member variable must be set exactly once! For example, in a constructor, field declaration, or intializer. (But you cannot set a final member variable in a method).

    Another consequence of making a member variable final relates to the memory model, which is important if you work in a threaded environment.

    =======================================下面是一篇转自网络的有关C++中const关键字的文章=========================

     今天在做一个趋势笔试题的时候,才让我有了系统把const关键字好好总结一下的冲动,因为这个关键词大大小小好多地方都出现过,出现频率非常高,而每次只是简短的把答案看了一下,没有真正将其整个用法弄透,马上要找工作了,为避免悲剧再次发生,不得不引起我的重视,这才有了这篇博文。

        首先,一看到const关键字,我们的第一反应就是定义常量。的确,这没有错,const就是constant的缩写嘛,但是事实上他的用法远不止这些。接下来我们一一来讲解。
        在C程序中,const的用法主要有定义常量、修饰函数参数和修饰函数返回值。而在C++程序中,它除了上述功能外,还可以修饰函数的定义体,定义类中某个成员函数为恒态函数,即不改变类中的数据成员。对于定义常量的用法,这里就不多说了,重点看一下修饰函数参数、修饰函数返回值以及修饰函数的定义体
        1)修饰函数参数
        首先如果该参数用于输出,那么无论是采用指针传递还是引用传递,都不能加const修饰。所以const只能用于修饰输入参数。这里又分三种情况:输入参数采用值传递还是指针传递还是引用传递。
        (1)如果采用值传递,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不需要加const 修饰。
         例如,对于函数void Func1(int x),写成void Func1(const int x)一点意义也没有。同理,对于void Func2(A a)也不需要写成void Func2(const A a),其中A为用户自定义的对象类型。
        (2)如果采用指针传递,那么加const可以防止函数体内部对该参数进行改变,起到保护作用。
         例如,假设StringCopy函数定义为:void StringCopy(char *strDest, const char *strSrc),那么,如果函数体试图改变strSrc的内容,编译器将报错。
        (3)如果采用引用传递,
         首先我们来说一下,为什么要引入引用传递这种方法。原因是:对于非内部数据类型的参数而言,象void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为void Func(A& a)。这样一来,根据引用传递的定义,只是借用了参数的别名,不需要产生临时对象。
          但是,这样一来,当函数体中改变了参数a的值后,相应的传递的原始值也会相应改变。所以如果不希望改变原始参数,只需要在前面加上const修饰,这样一来,函数最终定义为void Func(const A& a)。同理,是否应将void Func(int x) 改写为void Func(const int &x),以便提高效率?完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。
         总结一下const作为函数输入参数的用法:
         (1)对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
             (2)对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。
     
          2)修饰函数的返回值
          根据上面的思路,这里也分三种情况,即值传递、指针传递、引用传递。
          (1)如果函数返回值采用“值传递”方式,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。
         例如,不要把函数int GetInt(void) 写成const int GetInt(void)。同理不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。
        (2)如果函数返回值采用“指针传递”方式,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针
         例如,定义函数为:const char *GetString(void),那么char *str = GetString()将会出现编译错误。应该写成const char *str = GetString()。
        (3)如果函数返回值是采用“引用传递”方式,它的意义在于能提供啊效率,而这种方式使用场合并不多。这个时候,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错
         例如,对于类的重载赋值函数A & operate = (const A &other),如果不加cons修饰,则定义A a, b, c;(a = b) = c,程序合法,但是如果加上const修饰,即const A & operate = (const A &other),则程序会报错。
     
           3)修饰函数的定义体。
           定义const函数,只需要将const关键字放在函数声明的尾部任何不会修改类的数据成员的函数都应该声明为const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其它非const 成员函数,编译器将报错,这无疑会提高程序的健壮性。
        例如,以下程序中,类stack 的成员函数GetCount 仅用于计数,从逻辑上讲GetCount 应当为const 函数。编译器将指出GetCount 函数中的错误。
    class Stack
    {
    public:
      void Push(int elem);
      int Pop(void);
      int GetCount(void) const; // const 成员函数
    private:
      int m_num;
      int m_data[100];
    };
    int Stack::GetCount(void) const
    {
      ++ m_num; // 编译错误,企图修改数据成员m_num
      Pop(); // 编译错误,企图调用非const 函数
      return m_num;
    }
     
        到这里,const关键字的讲解就结束了。以下是几点使用const的几点规则。
        1) const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数。
        2) const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的。
        3) const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查。
        4) 然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的。 
     
     
    =================================这是另外一篇讲解const,static和const static的文章============================

     const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间。

          static表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。

          在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate=2.25;static关键字只能用于类定义体内部的声明中,定义时不能标示为static

          在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。

          const数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

          const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt。

    [cpp] view plaincopy
     
    1. class Test  
    2. {  
    3. public:  
    4.       Test():a(0){}  
    5.       enum {size1=100,size2=200};  
    6. private:  
    7.       const int a;//只能在构造函数初始化列表中初始化  
    8.        static int b;//在类的实现文件中定义并初始化  
    9.       const static int c;//与 static const int c;相同。  
    10. };  
    11.   
    12. int Test::b=0;//static成员变量不能在构造函数初始化列表中初始化,因为它不属于某个对象。  
    13. cosnt int Test::c=0;//注意:给静态成员变量赋值时,不需要加static修饰符。但要加cosnt  

          cosnt成员函数主要目的是防止成员函数修改对象的内容。即const成员函数不能修改成员变量的值,但可以访问成员变量。当方法成员函数时,该函数只能是const成员函数。

          static成员函数主要目的是作为类作用域的全局函数。不能访问类的非静态数据成员。类的静态成员函数没有this指针,这导致:1、不能直接存取类的非静态成员变量,调用非静态成员函数2、不能被声明为virtual

    关于static、const、static cosnt、const static成员的初始化问题:

    1、类里的const成员初始化:

    在一个类里建立一个const时,不能给他初值

    [cpp] view plaincopy
     
    1. class foo  
    2. {  
    3. public:  
    4.       foo():i(100){}  
    5. private:  
    6.       const int i=100;//error!!!  
    7. };  
    8. //或者通过这样的方式来进行初始化  
    9. foo::foo():i(100)  
    10. {}  

    2、类里的static成员初始化:

          类中的static变量是属于类的,不属于某个对象,它在整个程序的运行过程中只有一个副本,因此不能在定义对象时 对变量进行初始化,就是不能用构造函数进行初始化,其正确的初始化方法是:

    数据类型 类名::静态数据成员名=值;

    [c-sharp] view plaincopy
     
    1. class foo  
    2. {  
    3. public:  
    4.       foo();  
    5. private:  
    6.       static int i;  
    7. };  
    8.   
    9. int foo::i=20;  
    10. 这表明:  
    11. 1、初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆  
    12. 2、初始化时不加该成员的访问权限控制符privatepublic等  
    13. 3、初始化时使用作用域运算符来表明它所属的类,因此,静态数据成员是类的成员而不是对象的成员。  

    3、类里的static cosnt 和 const static成员初始化

          这两种写法的作用一样,为了便于记忆,在此值说明一种通用的初始化方法:

    [cpp] view plaincopy
     
    1. class Test  
    2. {  
    3. public:  
    4.       static const int mask1;  
    5.       const static int mask2;  
    6. };  
    7. const Test::mask1=0xffff;  
    8. const Test::mask2=0xffff;  
    9. //它们的初始化没有区别,虽然一个是静态常量一个是常量静态。静态都将存储在全局变量区域,其实最后结果都一样。可能在不同编译器内,不同处理,但最后结果都一样。  

    这是一个完整的例子:

    [cpp] view plaincopy
     
    1. #ifdef A_H_  
    2. #define A_H_  
    3. #include <iostream>  
    4. using namespace std;  
    5. class A  
    6. {  
    7. public:  
    8.       A(int a);  
    9.       static void print();//静态成员函数  
    10. private:  
    11.       static int aa;//静态数据成员的声明  
    12.        static const int count;//常量静态数据成员(可以在构造函数中初始化)  
    13.        const int bb;//常量数据成员  
    14. };  
    15. int A::aa=0;//静态成员的定义+初始化  
    16. const int A::count=25;//静态常量成员定义+初始化  
    17. A::A(int a):bb(a)//常量成员的初始化  
    18. {  
    19.       aa+=1;  
    20. }  
    21. void A::print()  
    22. {  
    23.       cout<<"count="<<count<<endl;  
    24.       cout<<"aa="<<aa<<endl;  
    25. }  
    26. #endif  
    27. void main()  
    28. {  
    29.       A a(10);  
    30.       A::print();//通过类访问静态成员函数  
    31.       a.print();//通过对象访问静态成员函数  
    32. }  
  • 相关阅读:
    跳跃表原理
    查看Oracle操作历史的试图
    【概念】为什么有时全表扫描比通过索引扫描效率更高
    oracle驱动表以及如何确定驱动表
    SpringBoot学习(三)-----配置Bean
    leetcode 面试题58
    leetcode 1365 有多少小于当前数字的数字
    leetcode 1342 将数字变成 0 的操作次数
    leetcode1313 解压缩编码列表
    leetcode 1071 字符串的最大公因子
  • 原文地址:https://www.cnblogs.com/Xiegg/p/3598942.html
Copyright © 2011-2022 走看看