jjzjj

c++ - 为什么在有效C++中以这种方式定义声明和定义?

coder 2023-05-31 原文

在Effective C++(第3版)中,第2项(constenuminline首选#define),特定于类的常量的代码段如下:

class GamePlayer {
private:
    static const int NumTurns = 5;    // constant declaration
    int scores[NumTurns];             // use of constant
    ...
};

然后,这本书(用我自己的话说)说static const int NumTurns = 5;不是定义,C++对于类成员通常是必需的,除非它是一个静态整数常数,其地址从未使用过。如果以上内容对于常量不是正确的,或者编译器出于任何原因坚持使用定义,则应在实现文件中按以下方式提供定义:
const int GamePlayer::NumTurns;    // definition of NumTurns; see
                                   // below for why no value is given

根据这本书(用我自己的话说),定义中没有给出任何值,因为它已经在声明中给出了。

这使我以为我已经知道的关于声明和定义的定义感到困惑(我在问这个问题之前在Google上进行了仔细检查):
  • 为什么static const int NumTurns = 5没有定义?此处NumTurns是否未初始化为5的值,并且不是同时存在声明和定义时称为初始化吗?
  • 为什么static整数常量不需要定义?
  • 为什么在没有定义值的情况下,第二个代码片段被认为是定义,但是包含该值的类内部的声明仍然不是一个(基本上回到我的第一个问题)?
  • 初始化不是定义吗?为什么这里没有违反“唯一定义”规则?

  • 此时我可能会感到困惑,所以有人可以从头开始对我进行再教育:为什么这两行代码声明和定义而不是另一行,并且那里有初始化的实例?初始化也是定义吗?

    信用:代码段直接从书中引用。

    编辑:附加引用What is the difference between a definition and a declaration?
  • 声明引入标识符,并键入
  • 定义实例化并实现

  • 所以,是的...这似乎不是这里发生的事情。

    编辑2 :我认为编译器可能通过不将其存储在内存中而仅将其内联替换为代码来优化静态整数常量。但是,如果使用了NumTurns地址,为什么声明不自动变为声明+定义,因为实例化已经存在了?

    编辑3 :
    (此编辑与原始问题关系不大,但仍然很出色。我将其放在此处,这样就无需将粘贴复制粘贴到下面每个答案的评论中。请在评论中回答我。 谢谢!)

    感谢您的回答。现在我的头脑更清楚了,但是编辑2的最后一个问题仍然存在:如果编译器在程序中检测到需要定义的条件(例如,程序中使用了&NumTurns),为什么它不自动重新解释static const int NumTurns = 5;作为声明和定义而不是仅声明?它具有程序中其他任何地方都有定义的所有语法。

    我来自学校的Java背景,因此不需要以上述方式为静态成员创建像这样的单独定义。我知道C++是不同的,但我想知道上面的原因为何。如果从未使用过地址,则将静态积分成员替换为内联听起来对我来说不是一种优化,而不是一项基本功能,所以我为什么要解决这个问题(在条件不存在时提供单独的语句作为定义)即使原始语句的语法就足够了也可以满足)而不是相反(编译器将原始语句视为定义,因为语法就足够了,编译器将其视为定义)?

    最佳答案

    免责声明:我不是标准专家。

    我建议您阅读以下两项:

    http://www.stroustrup.com/bs_faq2.html#in-class
    和:
    What is the difference between a definition and a declaration?

    Why is static const int NumTurns = 5 not a definition? Is NumTurns not initialized to a value of 5 here, and is it not that when a declaration and a definition occurs together, it is called an initialization?



    在高层,定义(而不是声明)实例化或实现实体(变量,类,函数)。对于变量,定义使变量分配在程序存储器中。对于功能-定义给出了可以编译为汇编指令的指令。*

    代码行
    static const int NumTurns = 5;
    

    默认情况下,不使NumTurns分配在程序存储器中,因此仅是声明。为了在程序存储器中创建(唯一的)NumTurns实例,您将必须提供所需的定义:
    const int MyClass::NumTurns;  
    

    Bjarne语录:

    You can take the address of a static member if (and only if) it has an out-of-class definition:


    class AE {
        // ...
    public:
        static const int c6 = 7;
        static const int c7 = 31;
    };
    
    const int AE::c7;   // definition
    
    int f()
    {
        const int* p1 = &AE::c6;    // error: c6 not an lvalue
        const int* p2 = &AE::c7;    // ok
        // ...
    }
    

    Why do static integral constants not require a definition?



    如果您想使用他们的地址,他们会这样做。

    Isn't initialization a definition?



    不可以。初始化是在某些实体(原始,对象)中设置初始值的 Action 。
    void someFunc (){
      int x; //x is declared, but not initialized
      int y = 0; //y is declared+ initialized.
    }
    

    Why is the "only one definition" rule not violated here?



    为什么会这样呢?整个代码中仍然有一个NumTurns。符号MyClas::NumTurns仅出现一次。定义仅使变量出现在程序存储器中。 ODR规则指定任何符号只能声明一次。

    编辑:
    问题基本上可以归结为“为什么编译器不能自行决定?”
    我不是委员会的成员,所以我不能给出完全合法的答案。
    我的聪明猜测是,让编译器决定事情不是C++的哲学。
    当您让编译器决定时,例如在哪里声明静态整数const,
    作为开发人员,我可能会提出以下问题:

    1)如果我的代码是仅 header 的库,会发生什么?
    我的编译器应在哪里声明变量?在第一个cpp文件中遇到?
    在包含main的文件中?

    2)如果编译器决定在哪里声明该变量,我是否有任何保证
    该变量将与其他静态变量(我已经手动声明)一起声明,因此保持
    缓存位置是否紧密?

    一旦我们批准“放任自流”,我们就会提出越来越多的问题。
    我认为这是Java和C++之间的根本区别。

    Java:让JVM发挥作用。
    C++:让开发人员进行分析。

    最终,在C++中,编译器检查所​​有内容是否有意义,并将代码转换为二进制代码,
    而不是开发人员而不是工作。
    *或字节码,如果您使用托管C++(boo ...)

    关于c++ - 为什么在有效C++中以这种方式定义声明和定义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34629876/

    有关c++ - 为什么在有效C++中以这种方式定义声明和定义?的更多相关文章

    1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    2. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

      我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

    3. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    4. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

      我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

    5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    7. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

      为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

    8. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    9. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    10. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

      我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

    随机推荐