C++ 非静态成员函数的非法调用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 非静态成员函数的非法调用相关的知识,希望对你有一定的参考价值。

我想通过ChtmlView来访问CDocument (都是已有的派生的类),调用SetTitle,然后发现g:\watravel\view.cpp(145): error C2352: “CWebDoc::SetTitle”: 非静态成员函数的非法调用
g:\watravel\doc.h(59) : 参见“CWebDoc::SetTitle”的声明

参考技术A 不太清楚你想干什么。。不过你的程序里错误很多。
error C2352: “std::vector<_Ty>::size”: 非静态成员函数的非法调用
这个错误是 i=vector<int>::size();要改成 i=v.size();

v的初始化应该用push_back。并在while语句里提供退出条件。

if ((it+1) == v.end())
break;
这个应该放在前面判断本回答被提问者和网友采纳
参考技术B 代码一:
[cpp] view plaincopyprint?
class Point
public:
void output()


static void init()


;
int main()

Point pt;
pt.init();
pt.output();

class Point
public:
void output()


static void init()


;
int main()

Point pt;
pt.init();
pt.output();
代码二:

[cpp] view plaincopyprint?
class Point
public:
void output()


static void init()


;
int main()

Point::init();
Point::output();//报错

class Point
public:
void output()


static void init()


;
int main()

Point::init();
Point::output();//报错

两个代码编译运行,代码一成功,代码二报错。错误信息:

test1.cpp(16) : errorC2352: “Point::output”: 非静态成员函数的非法调用。
错误信息提示:非法调用非静态成员函数Point::output。但为什么init函数的调用没有错呢?该函数是一个静态函数。静态成员函数和静态成员变量属于类本身,在类加载的时候,即为他们分配了空间,所以可以通过类名::函数名或类名:变量名来访问。而非静态成员函数和非静态成员变量属于对象的方法和数据,也就是应该首先产生类的对象,然后通过类的对象去引用。

代码三:

[cpp] view plaincopyprint?
#include
using namespace std;

class Point
public:
void output()


static void init()

x = 0;
y = 0;

private:
int x,y;
;
int main()

Point::init();
//Point::output();//报错

#include
using namespace std;

class Point
public:
void output()


static void init()

x = 0;
y = 0;

private:
int x,y;
;
int main()

Point::init();
//Point::output();//报错
运行报错:

1>e:\学习\程序练习\test1\test1\test1.cpp(11): error C2597: 对非静态成员“Point::x”的非法引用
1>e:\学习\程序练习\test1\test1\test1.cpp(12): error C2597: 对非静态成员“Point::y”的非法引用
在静态成员函数中非法引用Point对象的x和y数据成员。因为init是静态函数,不属于某个具体的对象,也就是说在还没有产生Point类的任一个具体的对象时,该函数就已经存在于程序的代码区了。但是,Point类的数据成员x和y还没有分配内存空间,这样,在init函数中对他们进行赋值操作会失败。也就是说,在静态成员函数中不能调用非静态成员,包括非静态成员函数和非静态成员变量。静态成员函数只能访问静态成员变量。
如果把上述代码中对x、y的赋值操作放在非静态函数中,就会成功。
但是,在非静态成员函数中可以调用静态成员函数。

在上述代码中,将Point类的数据成员x和y的定义之前加上static限定符,使他们成为静态成员变量。
staticintx,y;
编译运行,程序报错。错误信息:
1>test1.obj: error LNK2001: 无法解析的外部符号"private:static int Point::y" (?y@Point@@0HA)
1>test1.obj: error LNK2001: 无法解析的外部符号"private:static int Point::x" (?x@Point@@0HA)
这是链接时发生的错误。出错的原因是对于静态成员变量,必须对他们进行初始化,并且应在类的定义之外进行此操作。我们在Point类的定义之外,加上如下两条初始化语句:
[cpp] view plaincopyprint?
intPoint::x = 0;
intPoint::y = 0;
intPoint::x = 0;
intPoint::y = 0;
则运行成功。
参考技术C 多贴点代码上来吧。至少把出错的那一行贴出来吧。

非静态成员作为非静态成员函数的默认参数[重复]

【中文标题】非静态成员作为非静态成员函数的默认参数[重复]【英文标题】:Nonstatic member as a default argument of a nonstatic member function [duplicate] 【发布时间】:2011-05-31 04:16:18 【问题描述】:
struct X

   X():mem(42)
   void f(int param = mem) //ERROR
   
      //do something
   
private: 
   int mem;
;

谁能告诉我为什么这在 C++ 中是非法的?!也就是说,我知道这是一个错误,我知道这个错误是什么意思,我就是不明白为什么这会是非法的!

【问题讨论】:

@marcog:虽然我可能同意这有点相关,但我相信这根本不是重复的...... @Armen 那里接受的答案在一定程度上回答了您的问题:编译器在解析默认参数时不知道该实例。 不适用于静态成员函数,因为数据成员不能是虚拟的,所以对于实例方法来说非常模糊。解决方法对于过载来说是微不足道的。 @Armen 我认为期望它起作用是合理的,我看不出语言方面的原因。 “在编译时需要知道默认参数”不是我认为的原因。在上面的代码中,默认参数 在编译时是已知的——它是对成员 some_member_variable 的类 std::string.size() 的调用。这就是所有需要的。重载解析是在不考虑默认参数的情况下完成的(否则我们会有循环依赖)。所以当我们替换默认参数时,我想我们知道我们需要触摸哪个对象的成员。 更新:@user396672 提供了一个有见地的语言原因。 【参考方案1】:

您的代码(简化版):

struct X

   int mem;
   void f(int param = mem); //ERROR
;

您想使用非静态成员数据作为成员函数参数的默认值。想到的第一个问题是:默认值mem 属于类的哪个具体实例

X x1 = 100;  //mem = 100
X x2 = 200;  //mem = 200

x1.f(); //param is 100 or 200? or something else?

您的答案可能是100,因为f() 是在具有mem = 100 的对象x1 上调用的。如果是这样,那么它需要实现将f() 实现为:

void f(X* this, int param = this->mem);

这反过来要求在初始化其他参数之前首先初始化第一个参数。但是 C++ 标准没有指定函数参数的任何初始化顺序。因此这是不允许的。出于同样的原因,C++ 标准甚至不允许这样做:

int f(int a, int b = a); //§8.3.6/9

事实上,§8.3.6/9 明确表示,

每个默认参数都被评估 调用函数的时间。 订单 函数参数的评估是 未指定因此,参数 的函数不得用于 默认参数表达式,即使 他们没有被评估。

这部分的其余部分很有趣。


一个与“默认”参数相关的有趣话题(虽然与这个话题无关):

Default argument in the middle of parameter list?

【讨论】:

接受@user396672 的回答的整洁和合乎逻辑的解释 @Armen:我添加了一个指向另一个主题的链接。 :D 但这与所讨论的问题没有任何关系,不是吗? :) 如果您将实现(这只是一种可能的实现)转为void f(X* this = self, int param = self-&gt;mem);,则评估顺序并不重要。【参考方案2】:

必须在编译时知道默认参数。当您谈论诸如函数调用之类的事情时,即使返回值不是,该函数在编译时也是已知的,因此编译器可以生成该代码,但是当您默认使用成员变量时,编译器不会不知道在编译时在哪里可以找到该实例,这意味着它实际上必须传递一个参数 (this) 才能找到 mem。请注意,您不能执行 void func(int i, int f = g(i)); 之类的操作,这两者实际上是相同的限制。

我也觉得这个限制很傻。但是,C++ 充满了愚蠢的限制。

【讨论】:

+1 表示该问题实际上与类甚至 OO 无关。但是,我认为限制涉及评估上下文而不是评估时间 -1 代表"Default arguments have to be known at compile-time"。这不是真的。 糟糕。我不能投反对票,因为我已经在 2010 年 12 月投了赞成票。那我错了! 我认为@user396672 说服了我。它需要在任何默认参数之前评估对象表达式。 @Armen:哦……我又读了一遍,现在是完整的答案。我同意这个推理。我也是这么想的。赞成。【参考方案3】:

正如 DeadMG 上面提到的,类似

void func(int i, int f = g(i))

出于同样的原因是非法的。然而,我认为这不仅仅是一个愚蠢的限制。为了允许这样的构造,我们需要限制函数参数的评估顺序(因为我们需要在 this->mem 之前计算 this),但是 c++ 标准明确拒绝对评估顺序的任何假设。

【讨论】:

【参考方案4】:

重复问题中公认的答案是为什么,但标准也明确说明了为什么会这样:

8.3.6/9:

" 示例:以下示例中 X::mem1() 的声明格式不正确,因为没有为用作初始值设定项的非静态成员 X::a 提供对象。

int b;
class X
  int a;
  int mem1(int i = a);    // error: nonstatic member a
                          // used as default argument
  int mem2(int i = b);    // OK: use X::b
  static int b;
;

然而,X::mem2() 的声明是有意义的,因为访问静态成员 X::b 不需要任何对象。类、对象和成员在第 9 节中描述。 "

...由于此时不存在提供解析X::a 值所需的对象的语法,因此实际上不可能使用非静态成员变量作为默认参数的初始值设定项。

【讨论】:

【参考方案5】:

ISO C++ 8.3.6/9 节

不应在默认参数表达式中使用非静态成员,即使它 不求值,除非它作为类成员访问表达式 (5.2.5) 的 id 表达式出现,或者除非它用于形成指向成员的指针 (5.3.1)。

还可以查看该部分中给出的示例。

【讨论】:

否,默认参数可能不知道编译时间。例如, void f(int x = g()) 其中 g 是一个全局函数是可以的【参考方案6】:

出于一个原因,因为f 是公开的,而mem 是私有的。因此,代码如下:

int main()  
    X x;
    x.f();
    return 0;

...将涉及外部代码检索 X 的私人数据。

除此之外,它还会(或至少可以)使代码生成变得有点棘手。通常,如果编译器要使用默认参数,它会获取将作为函数声明的一部分传递的值。生成代码以将该值作为参数传递是微不足道的。当您可能传递一个对象的成员(可能是任意深度嵌套)然后添加诸如它可能是模板中的从属名称之类的东西时,这可能(例如)命名另一个对象并转换为正确的目标输入,你就有了一个让代码生成变得相当困难的秘诀。我不确定,但我怀疑有人考虑过这样的事情,并决定最好保持保守,并且可能稍后再开放,如果找到了一个很好的理由这样做.考虑到我看到它出现问题的次数,我猜它会保持这种状态很长一段时间,仅仅是因为它很少引起问题。

【讨论】:

这是错误的。 11/7 - “默认参数表达式(8.3.6)中的名称在声明点绑定,并且在该点而不是在任何使用点检查访问......” 【参考方案7】:

编译器必须知道地址以在编译时维护默认值。非静态成员变量的地址在编译时是未知的。

【讨论】:

int add_random(int k, int m=rand()) return k+ m; 是合法的,尽管在编译时随机值和地址都是未知的。枚举默认值可能根本没有地址。 你确定吗?我的意思是,每次我启动一个可执行文件时,参数 m (你定义为: m = rand() )在我的机器上都有相同的值。似乎它被定义一次(在编译时),它根本不会改变值。枚举是常量——它们的值在编译时是已知的。【参考方案8】:

由于所有其他答案都只是讨论问题,我想我会发布一个解决方案。

在没有默认参数的其他语言中使用(例如 C# pre 4.0)

只需使用重载即可提供相同的结果:

struct X

   X():mem(42)
   void f(int param)
   
      //do something
   
   void f()
   
      f(mem);
   
private: 
   int mem;
;

【讨论】:

【参考方案9】:

默认参数在不同的上下文中分两个不同的步骤进行评估。 首先,默认参数的名称查找是在声明的上下文中执行的。 其次,默认参数的评估是在实际函数调用的上下文中执行的。

为避免实现过于复杂,对可用作默认参数的表达式应用了一些限制。

不能使用具有非静态生命周期的变量,因为它们在调用时可能不存在。 不能使用非静态成员变量,因为它们需要(隐式)this-&gt; 限定条件,通常无法在调用站点进行评估。

【讨论】:

以上是关于C++ 非静态成员函数的非法调用的主要内容,如果未能解决你的问题,请参考以下文章

visual C++中error C2352是啥意思

visual C++中error C2352是啥意思

mfc 时钟 OnTimer 非静态成员函数的非法调用

非静态成员作为非静态成员函数的默认参数[重复]

C++使用模板类调用非静态成员函数

C++之静态