c++ 错误:非常量静态数据成员必须在行外初始化

jtw3ybtb  于 2023-05-24  发布在  其他
关注(0)|答案(3)|浏览(449)
class Solution {
    public:

     static int m=INT_MIN; // it shows error: non-const static data member must 
                               be initialized out of line.(why?)
                                  using "int m=INT_MIN" is fine. 
      int func(TreeNode*root){
        if(root==NULL){
            return 0;
        }

        int l=max(func(root->left),0);
        int r=max(func(root->right),0);

        m=max(l+r+root->val,m);

        return max(l,r)+root->val;

    }

    int maxPathSum(TreeNode* root) {

        if(root==NULL)
        {
         return 0;
        }
        m=INT_MIN;
        int x=func(root);
        return m;

    }
};

我需要更新变量m的值。因此,我使用static int数据类型。但接下来的错误来了。使用int而不是static int工作正常。为什么static int会出错?

wn9m85ua

wn9m85ua1#

Bjarne Stroustrup在这里解释说:
类通常在头文件中声明,并且头文件通常包含在许多转换单元中。然而,为了避免复杂的链接器规则,C要求每个对象都有唯一的定义。如果C允许在类内定义需要作为对象存储在内存中的实体,那么这条规则就被打破了。
正如Stroustrup所说,每个类都需要一个唯一的定义。现在,正如我们所知,静态成员直接与它们的类相关联。现在考虑两种情况:

  1. static成员也是constant,那么它的初始化是允许内联的,因为编译器可以进行自己的优化,并将此成员视为编译时常量,因为它可以保证其值永远不会更改。因此,由于此成员的值是固定的,因此与此成员关联的类的定义也是固定的。因此,允许内联初始化。
  2. static成员不是常量。然后它的值可以在程序执行期间稍后更改。因此,编译器不能对此成员进行编译时优化。因此,为了防止在加载类时尝试初始化此类成员时可能出现的复杂性,不允许对此类成员进行内联初始化。

**PS:**当我第一次听到这个概念时,我也很困惑,因为它不符合程序员所期望的正交性原则。正交性的原理将说明,由于我们可以合并intstatic;以及intconst,我们应该能够以类似的方式写入static const intstatic int。但是这里的情况是一种情况的例子,其中语言的开发者必须给予语言用户的正交性,以换取编译过程的简单性。

看看正交性here的概念

6qftjkof

6qftjkof2#

回答OP的问题“为什么”

class Solution {
  public:
    int m = INT_MIN;
};

很好但是

class Solution {
  public:
    static int m = INT_MIN;
};

不是:
简而言之:用static作为数据成员的前缀从根本上改变了它的含义。

没有static,成员变量是类的一部分,每个示例将为该成员变量提供单独的存储。
**使用static**时,成员变量只有类的作用域,但只有一个全局存储。

分别地,初始化也具有不同的含义。
对于非静态成员变量,它提供了构造函数可以使用(或重写)的默认初始化。
示例演示:

#include <iostream>

enum ArgCase1 { Case1 };
enum ArgCase2 { Case2 };

class Solution {
  public:
    int m = 123;
    
    Solution() = default; // will use m(123) implicitly
    Solution(ArgCase1) { } // will use m(123) implicitly
    Solution(ArgCase2): m(456) { } // default of m ignored
};

#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ 

int main()
{
  DEBUG(Solution sol);
  std::cout << "sol.m: " << sol.m << '\n';
  DEBUG(Solution sol1(Case1));
  std::cout << "sol1.m: " << sol1.m << '\n';
  DEBUG(Solution sol2(Case2));
  std::cout << "sol2.m: " << sol2.m << '\n';
}

输出:

Solution sol;
sol.m: 123
Solution sol1(Case1);
sol1.m: 123
Solution sol2(Case2);
sol2.m: 456

Live Demo on coliru
对于static成员变量,初始化将是危险的。假设一个类在一个包含多次的头文件中声明,这将导致违反One Definition Rule
在以前,通常在头文件中 * 声明 * static成员变量,但在.cpp文件(代表translation unit)中 * 定义 * 它。
示例:
solution.h

#ifndef SOLUTION_H
#define SOLUTION_H

class Solution {
  public:
    static int m;
};

#endif // SOLUTION_H

solution.cc

// header of this module:
#include "solution.h"

int Solution::m = 123;

Live Demo on coliru
从C++17开始,有了一个新的替代方案--使用关键字inline
来自cppreference.com -静态成员-静态数据成员
静态数据成员可以内联声明。内联静态数据成员可以在类定义中定义,并且可以指定初始化式。它不需要类外定义
示例:
solution.h

#ifndef SOLUTION_H
#define SOLUTION_H

class Solution {
  public:
    inline static int m = 123;
};

#endif // SOLUTION_H

Live Demo on coliru
优点是不需要.cpp文件,即实际上,X1 M11 N1 X可以被提供为仅报头源。

5w9g7ksd

5w9g7ksd3#

你可以做的是在你调用相应函数的地方,首先,初始化一个int,然后通过地址将该int传递给类方法。相应地编辑类方法定义。

int main(){
     int m=INT_MIN;
     Solution *solution = new Solution;
     solution->func(root,&m);
}

相关问题