首页 > 试题广场 >

以上程序中,下列哪个函数调用会有问题()

[单选题]
using namespace std;

class A {
public:
    void FunctionA() {cout << "FunctionA" << endl;}
    virtual void FunctionB() {cout << "FunctionB" << endl;}
    static void FunctionC() {cout << "FunctionC" << endl;}
};

class B : public A {
public:
    void FunctionB() {cout << "FunctionB" << endl;}
    int FunctionD() {cout << "FunctionD" << endl;}
};

int main() {
    B *b = nullptr;
    b->FunctionA();
    b->FunctionB();
    b->FunctionC();
    b->FunctionD();
    return 0;
}
以上程序中,下列哪个函数调用会有问题()
  • b->FunctionD();
  • b->FunctionB();
  • b->FunctionA();
  • b->FunctionC();
因为虚函数本质上是通过类对象的虚表进行访问,而且类的成员函数除了虚函数,其他都不存储在类当中,因此类对象不存在的情况下,无法使用虚函数,其他函数都可以正常访问(前提是这些函数都没有存取类对象的成员变量)
发表于 2017-09-06 16:51:22 回复(2)
搬运别人的答案,链接在下,其实《C++对象模型》有讲,奈何没能灵活理解啊。。。
要理解这个的话。。。成员函数其实可以认为是一个普通的函数,比如
1
2
3
4
class A{
public:
    void func(int x) { cout<<"hello, A. x="<<x<<endl; }
};

在编译器看来,大概就长这个样子吧:

1
void A_func(A* this, int x) { cout<<"hello, A. x="<<x<<endl; }

你平时使用成员函数的时候,大概就是这样的:

1
2
A a;
a.func(2);

其实在编译器看来,是这个样子的:

1
A_func(&a, 2);

如果这么说的话,也许你就理解了,为什么对象是NULL的时候还可以调成员函数:

1
2
3
4
5
6
A *pa = NULL;
pa->func(2);
//在编译器看来就好像是 A_func(pa, 2);且pa==NULL
 
((A*)NULL)->func(2);
//在编译器看来就好像是 A_func( ((A*)NULL), 2);

-----我是分割线-----

上面的例子中func函数里并没有使用成员变量。考虑有成员变量并且在成员函数里使用的情况,就会不一样了:

1
2
3
4
5
6
class A{
private:
    int y;
public:
    void func(int x) { y = x; }
};

注意此时y是成员变量,编译器会自动给它加上this->,也就是

1
void A_func(A* this, int x) { this->y = x; }

此时正常的情况就不用说了,说说用NULL对象指针调用成员函数的情况:

1
2
3
4
5
6
7
8
A *pa = NULL;
pa->func(2);
//在编译器看来就好像是 A_func(pa, 2);且pa==NULL
 
((A*)NULL)->func(2);
//在编译器看来就好像是 A_func( ((A*)NULL), 2);
 
//好吧我承认这段代码跟上面的一毛一样啦!

此时程序会崩溃!为什么?因为this指针是NULL,而你访问了它的y变量!

----又是我哈哈哈-----

结论:

  1. 通过对象调用成员函数,对象的指针会被传入函数中,指针名称为this

  2. 因此NULL对象指针也可以调用成员函数

  3. NULL对象指针调用成员函数时,只要不访问此对象的成员变量,则程序正常运行

  4. NULL对象指针调用成员函数时,一旦访问此对象的成员变量,则程序崩溃

下面是我加的
同理,当在调用虚函数时,对于一个虚拟函数调用
如b->FunctionB();
将会被转化成 (*b->vptr[1])(b),vptr表示由编译器产生的指针,指向virtual table,而1是virtual table slot的索引值,关联到FunctionB函数,此时b为NULL,因此肯定无法访问,出现错误。
发表于 2017-09-03 22:11:25 回复(9)
一、new创建类对象与不new区别
下面是自己总结的一些关于new创建类对象特点:
new创建类对象需要指针接收,一处初始化,多处使用
new创建类对象使用完需delete销毁
new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
new对象指针用途广泛,比如作为函数返回值、函数参数等
频繁调用场合并不适合new,就像new申请和释放内存一样
二、new创建类对象实例
1、new创建类对象例子:
CTest* pTest = new CTest();
delete pTest;
pTest用来接收类对象指针。
不用new,直接使用类定义申明:
CTest mTest;
此种创建方式,使用完后不需要手动释放,该类析构函数会自动执行。而new申请的对象,则只有调用到delete时再会执行析构函数,如果程序退出而没有执行delete则会造成内存泄漏。
2、只定义类指针
这跟不用new申明对象有很大区别,类指针可以先行定义,但类指针只是个通用指针,在new之前并为该类对象分配任何内存空间。比如:
CTest* pTest = NULL;
但使用普通方式创建的类对象,在创建之初就已经分配了内存空间。而类指针,如果未经过对象初始化,则不需要delete释放。
3、new对象指针作为函数参数和返回值
#include <iostream>
using namespace std;
class A
{
public:
	void FunctionA() { cout << "FunctionA" << endl; }
	virtual void FunctionB() { cout << "FunctionB" << endl; }
	static void FunctionC() { cout << "FunctionC" << endl; }
};
class B :public A
{
public:
	void FunctionB() { cout << "FunctionB2" << endl; }
	void FunctionD() { cout << "FunctionD" << endl; }
};
class E
{
public:
	void FunctionE() { cout << "FunctionE" << endl; }
};
int main()
{
	//B *b = NULL;
	B *b = new B();
	b->FunctionA();
	//new B(),会将虚函数表头指针放在内存首地址处,这里并没有new B(),
	//所以,表头指针指向未知区域,找FunctionB所对应的偏移从而调用FunctionB
	//显然会报错
	b->FunctionB();
	b->FunctionC();
	b->FunctionD();
	return 0;
}
编辑于 2017-08-28 22:09:00 回复(0)
访问虚函数,必须有对象的存在。原因:对象中存放虚函数表指针
发表于 2017-09-13 13:38:42 回复(0)
虚函数通过类对象的虚函数表访问,空类指针,没有指向任何一个对象,找不到对应的虚函数;静态函数和静态成员不能被继承,静态函数实际是“加上了访问控制的全局函数”,静态变量实际是“加上了访问控制的全局变量”,通过子类对象可以访问到父类的静态函数或静态变量实际是访问了一块共享内存,并不是继承下来的,且静态函数不能为虚函数
发表于 2018-04-04 14:03:49 回复(1)
functionD也会出问题啊   返回值是int但是函数内没有返回任何东西哦
发表于 2019-05-06 10:38:14 回复(1)
测试了,B错。问题出在B *b = NULL;如果改成B *b = new B();,4个调用都是正常的。

发表于 2017-08-18 10:36:22 回复(0)
啥头像
对于ACD为什么是对的, b->FunctionD()相当于 FunctionD(this),只要函数里没有用到this的相关东西就不会出错
发表于 2018-04-09 19:17:30 回复(0)
类的空指针调用该类的成员函数,只要没有解引用该指针,程序会正常执行。(①虚函数,②成员变量都在该类对象上,所以只要用该空指针访问①②,程序都会出错)
发表于 2018-01-15 17:53:44 回复(0)
A选项也有问题,b->FunctionD();缺少int类型返回值
发表于 2018-09-08 01:30:22 回复(1)
-----函数重载:函数名称相同,参数和返回值至少有一个不同; -----函数重写:也叫函数覆盖,子类重新定义父类中相同名称和参数的虚函数,函数体征相同,具体实现不同,主要出现在继承关系中(基类中必须有virtual关键字,子类中不一定要有); -----函数隐藏:也叫重定义,指子类屏蔽了与其同名的基类函数,可通过类域找回(同名,参数不同,不管基类有无virtual,被隐藏;同名,参数相同,基类无virTual,被隐藏) -----类的成员函数不与具体对象绑定,程序编译后,成员函数的地址已经确定,所有对象公用一份成员函数体,区分的关键在于this 指针,故空对象指针也能调用成员函数。需要注意的是,类的静态成员函数只能访问静态成员变量,没有this ,不需要经过类的对象的调用,但是使用类的对象来调用也是合法的。
编辑于 2017-09-12 09:49:43 回复(1)
关于本题的详细答案,请看此文章:http://blog.sina.com.cn/s/blog_155aff35b0102ww0m.html
编辑于 2017-09-08 15:27:44 回复(0)
funA时初始化了。再调用funB找不到虚函数表地址。
发表于 2017-08-15 09:50:48 回复(0)
#include 
using namespace std;
class A
{
public:
    void FunctionA(){ cout << "FunctionA" << endl; }
    virtual void FunctionB(){ cout << "FunctionB" << endl; }
    static void FunctionC(){ cout << "FunctionC" << endl; }
};
class B :public A
{
public:
    void FunctionB(){ cout << "FunctionB2" << endl; }
    void FunctionD(){ cout << "FunctionD" << endl; }
};
class E
{
public:
    void FunctionE(){ cout << "FunctionE" << endl; }
};
int main()
{
    B *b = NULL;
    b->FunctionA();
    //new B(),会将虚函数表头指针放在内存首地址处,这里并没有new B(),
    //所以,表头指针指向未知区域,找FunctionB所对应的偏移从而调用FunctionB
    //显然会报错
    b->FunctionB();    
    b->FunctionC();
    b->FunctionD();
    return 0;
}
编辑于 2017-08-11 19:58:38 回复(0)
动态绑定不可以(虚函数),静态成员函数可以。
发表于 2017-08-11 14:58:58 回复(3)
当派生类重写基类的函数时,应将基类的函数声明为虚函数,这样程序会根据引用或指针指向对象类型而不是引用或指针的类型来选择函数,本题指针指向的类型为空,所有程序不知道调用哪个FunctionB()。
发表于 2021-05-13 18:16:27 回复(0)
访问虚函数,必须有对象的存在。原因:对象中存放虚函数表指针
发表于 2020-11-28 12:28:05 回复(0)
使用虚函数就是使用了vfptr,而vfptr是成员变量,使用成员变量需要this指针,而类指针置为空是没有this指针的
发表于 2023-07-08 21:59:36 回复(0)
因为这里是已经重写了虚函数,是动态绑定,但是需要vptr指针,此时没有调用构造函数,因此没有vptr指针,因此也就没法调用虚函数
发表于 2022-06-02 15:47:46 回复(0)
注意空指针 对象内存模型vptr
发表于 2022-02-07 23:03:10 回复(0)