VC用了N久敲代码无数.....今天给一新手调代码, 无意中发现......
class MyTest{};int main(){ //新手最易犯的毛病之1
MyTest mytest();//此乃函数声明, 非对象定义!
         //正确的方法是
         //MyTest mytest;
          return 0;
}
这倒没啥, 问题是他拿着下面的代码回来问我.
class MyTest{};int main()
{ MyTest mytest(MyTest());
return 0;
}如果是个函数声明, mytest的原形为MyTest mytest(MyTest), 参数表里的括号是干啥的?
如果是个函数调用, 开头的MyTest是干啥的?
所以两种可能都不对, 代码是错误的. 用VS2008编译一下竟然通过了, 不过有个warning:
warning C4930: “MyTest mytest(MyTest (__cdecl *)(void))”: prototyped function not called (was a variable definition intended?)
查找CSDN, 原来VC把MyTest()解释为一个函数指针囧....
mytest就是个函数, 原形相当于MyTest mytest(MyTest (*pfun)(void));//注意下面的MyTest一个是类, 一个是函数, 重名的...class MyTest{
public:
MyTest(){ printf("construct of class MyTest\n"); }
};//MyTest的类型为void (*)(void), 因为是匿名的所以无法调用...
MyTest mytest(MyTest()){ printf("function: mytest\n");
//定义一个MyTest对象返回.
return MyTest();
}//这个函数当作mytest参数...
MyTest MyTest(void){ printf("function: MyTest\n");
//这是啥? 不是定义对象哦, 乃无限递归也!
return MyTest();
}int main()
{
//调用mytest...
mytest(MyTest);
//你想定义一个MyTest吗?
MyTest mt; //产生错误, 因为函数与类名相同.....
class MyTest mt//这样才正确.....
return 0;
}ps 显然如果类名和函数名相同, VC会用函数名覆盖类名, ISO C++里也是这么规定的吗?????

解决方案 »

  1.   

    楼主,我用VC6来试这个代码.
    class MyTest{};
    int main(){
        MyTest mytest(MyTest());
        return 0;}
    很正常,先调用构造,建一个MyTest()作为参数,再调用复制构造,把参数MyTest()传给mytest.但是我用VS2008,同样的代码,正如楼主那样,报警告,函数原型没被调用(你是想定义一个变量吗?)
    但是我如何才可以告诉编译器,这是一个对象的定义,而非函数的声明呢?
      

  2.   

    今天真开眼界了.第二个问题,编译器把 MyTest() 当作是函数的指针,在VC6上也试过了一下.
    VS2008里面,MyTest mytest(MyTest()){}  MyTest()当作是 MyTest (*)()我在VC6里写MyTest mytest(MyTest()){}  会报错,function-style initializer appears to be a function definition
    在VC6里,显式地写成MyTest mytest(MyTest (*)()) {} 这样才行.
    结果跟楼主在VS2008一样,认为参数是一个函数指针,该指针的返回值是MyTest,参数表空.到底C++ 定义函数指针的写法是怎样的呢?VC6与VS2008有不同的标准?VS2008允许简化一些?
      

  3.   

    我感觉应该是显示定义(参数和返回值都写全了), VC是自做主张...
    不过ISO C++太厚了我真有点懒的翻...
      

  4.   

    找个其他的C++编译器再试一下,看这个例子,感觉VS2008没VC6那么严格.
      

  5.   

    用DevCpp也试过了一下,class MyTest{
    public:
    MyTest(){
    printf("MyTest Ctor\n");
    }
    MyTest(const MyTest& rhs){
    printf("MyTest Copy Ctor\n");
    *this = rhs;
    }
    };
    int main(int argc, char* argv[])
    {
    MyTest mytest(MyTest()); getchar();
    return 0;
    }
    这种写法,只有VC6是当作定义对象并执行复制构造.
    VS2008,DevCpp都当作是一个函数的声明,并且参数是 MyTest (*)(void)
    我跟楼主一样,对C++越来越孤陋寡闻了.
      

  6.   


    class MyTest{
    public:
    MyTest(){
    printf("MyTest Ctor\n");
    }
    MyTest(const MyTest& rhs){
    printf("MyTest Copy Ctor\n");
    *this = rhs;
    }
    MyTest(int a){
    printf("Ctor With Int\n");
    }
    void Func(){
    printf("Func\n");
    }
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
    MyTest temp(1);
    MyTest mytest(temp);
    MyTest mytest1(MyTest(1)); getchar();
    return 0;
    }
    加了一个带参的构造,编译器终于认为这是对象的定义,而非函数的声明了,但还是有诡异的事情.
    MyTest temp(1);  上面2句应该等价于下面一句.
    MyTest mytest(temp);
    MyTest mytest1(MyTest(1));
    但实际并不是这样,在VC6,上面2句的确等价于下面一句,先执行带参构造,再执行复制构造.
    但在VS2008与DevCpp里面,结果一样,都是只执行了带参构造,没有执行复制构造.
    查看一下汇编的代码.
    MyTest mytest1(MyTest(10)); 这2行C代码,对应的汇编是这样的.
    MyTest mytest2(10);
    大家的汇编一样.
    MyTest mytest1(MyTest(10));
    00413694  push        0Ah  
    00413696  lea         ecx,[mytest1] 
    00413699  call        MyTest::MyTest (4111D1h) 
    MyTest mytest2(10);
    0041369E  push        0Ah  
    004136A0  lea         ecx,[mytest2] 
    004136A3  call        MyTest::MyTest (4111D1h) 而VC6的汇编是下面这样的.
    28:       MyTest mytest(MyTest(1));
    00401278   push        1
    0040127A   lea         ecx,[ebp-8]
    0040127D   call        @ILT+20(MyTest::MyTest) (00401019)
    00401282   push        eax
    00401283   lea         ecx,[ebp-4]
    00401286   call        @ILT+0(MyTest::MyTest) (00401005)
    看来是VS2008对代码有自己的优化,所以产生了与VC6不同的结果.
      

  7.   

    万恶的C++与C++编译器.
    万一我在构造跟复制构造里搞了些什么东西,编译器给出的响应又不一样,有些编译器执行了复制构造,有些又没执行,release 与 debug 说不定也会出不同的优化.
    我现在是越来越头晕了.
      

  8.   

    你说的这个不是万恶是福利.....
    MyTest mytest1(MyTest(1));
    MyTest(1)是个临时对象除了拷贝别无它用, 公然的浪费...
    象样点的编译器都会跳过拷贝构造函数直接调用相应的构造函数, 
    但当参数为非临时对象时就会调用拷贝构造函数了. 此乃NRVO优化是也.
    不过NRVO在一些情况下没办法进行优化, 所以C++0x隆重推出右值引用就是解决类似问题的.
      

  9.   


    #include <iostream>
    using namespace std;class MyClass{public:
    MyClass(){ cout << "Default constructor" << endl; }
    MyClass(const MyClass&){ cout << "Copy constructor" << endl; }
    MyClass(int){ cout << "Constructor with a int parameter" << endl; }};int main(){ MyClass m1; // default constructor
    MyClass m2 = m1; // same as below
    MyClass m3(m2); // copy constructor
    // 
    MyClass m4(1); // call MyClass(int) by NRVO return 0;
    }
      

  10.   

    抱歉17L写错了, 看这个...
    #include <iostream>
    using namespace std;class MyClass{public:
    MyClass(){ cout << "Default constructor" << endl; }
    MyClass(const MyClass&){ cout << "Copy constructor" << endl; }
    MyClass(int){ cout << "Constructor with a int parameter" << endl; }};int main(){ MyClass m1; // default constructor
    MyClass m2 = m1; // same as below
    MyClass m3(m2); // copy constructor
    MyClass m4(MyClass(1)); // MyClass(int) by NRVO return 0;
    }
      

  11.   

    上网搜了一下NRVO,现在明白了,原来是这样.
    福利的同时,也有其副作用,导致了不一致的程序行为.楼主高人,加个QQ,以后多联系了.QQ 644832501
      

  12.   

    C++如些庞大,为程序员提供了有关编程概念的绝大部分支持,以致于完全学精通几乎是不可能的.
    因此C++是令人丧气的,学得越多越感觉自己对很多东西其实是模糊的.
    ----事实上C++标准委员会的成员之间也有分歧,以致C++0x一直无法完成.
    个人看法是:使用自己熟悉的C++特性来完成自己的工作.
      

  13.   

    如果需要执行拷贝构造函数的某些代码NRVO确实有副作用, 比如使用一个类内static变量维护拷贝记数.
    这时就要小心了,呵呵.
    不过这种需求很小, 拷贝构造函数还是尽量完成单纯的拷贝动作吧, 或者使用非临时变量强制调用拷贝构造函数.
    总体来说利大于弊吧. 期待C++0x的右值引用.
    高人不敢, 互相交流吧. 暂时上不了Q上了加你~~~~~~`
      

  14.   

    SDN, 原来VC把MyTest()解释为一个函数指针囧