class myclass{
public: void hello(){
std::cout<<"Hello"<<"\n";
}
}
myclass* mc = new myclass;
mc->hello();
函数是hello
动态分配的,还是非动态分配的?
class myclass{
public: void hello(){
std::cout<<"Hello"<<"\n";
}
}
myclass* mc = new myclass;
mc->hello();
函数是hello
动态分配的,还是非动态分配的?
函数根本没有“分配”。在底层,非虚拟函数与类没有固有的物理连接。成员函数只是普通独立函数之上的一层薄薄的语法糖。例如,在您的情况下,成员函数hello
被转换为普通的独立函数
void myclass_hello(myclass *this) {
std::cout<<"Hello"<<"\n";
}
当你打电话时
mc->hello();
编译器本质上将此调用替换为
myclass_hello(mc);
在 C++ 的早期,当许多 C++ 编译器仅作为 C 编译器的前端实现时,这正是 C++ 成员函数被翻译成 C 函数的方式。
成员函数和普通独立函数在“分配”方面没有区别。无论您创建了多少该类型的对象,都只存在一个成员函数实例。
使用虚函数,事情会变得有点复杂,但这仍然不会产生对成员函数“分配”的任何需求。每个版本的虚成员函数只有一个实例,并且它仍然独立于实际对象而存在。
函数不是对象(至少在 C++ 中),并且没有生命周期。他们就在那里。永远永远。(同样,从 C++ 的角度来看。)
当您深入了解 c++ 的内部结构时,您会发现编译器对代码做了各种疯狂的事情,例如完全更改函数的名称以支持函数重载。函数不是 C++ 中的对象,而是对函数表的引用。我认为您想知道的是,如果将方法放在类之外会更好,因为该类的每个实例都没有“副本”。事实是,您没有得到函数的“副本”,而是得到了指针变量的副本?答案是否定的,c++ 只会分析代码并将单个函数调用放在您使用的任何地方。在将代码中的变量名称转换为实际内存位置时,将考虑任何“this”或实例数据的引用。
hello 方法的机器代码是在编译时生成的。由于它不是虚方法,编译器可以决定如何直接调用它。等效代码可能是:
struct MyStruct {};
void hello(MyStruct* this)
{
std::cout << "Hello" << std::endl;
}
int main(int, char**)
{
MyStruct* ms = new MyStruct;
hello(ms);
return 0;
}
涉及函数时不分配内存。
这些只是可执行文件中的一堆代码
实际代码只有一个实例,hello
它在可执行文件中/在程序运行时加载到内存中。类(对象)的所有实例都使用代码的一个实例。
它完全取决于上下文,更准确地说它是什么类型的函数。您永远不会分配函数,因为它是代码而不是数据,唯一可以动态的是基于每个类调用的函数,这些是虚函数。
这是一个普通的类成员函数:
void MyClass::CallFunction()
{
}
实际上,编译器和许多 C++ 编译器都会这样做,添加一个通常“不可见”但同时可访问的函数参数,它通常是列表中的第一个,类对象的内存指针 *this。
void MyClass::CallFunction(MyClass *this)
{
}
如果您声明成员函数static,您基本上创建了一个在类范围内但不属于类的函数,因此它没有 this 指针。
由于作用域,编译器会自动引用您的类变量,因此您不必这样做,因此可以访问属于类对象的任何内容
this->classVariable = 0;
对于虚函数,编译器创建了一个由编译器处理的 vtable,因此函数覆盖和多态性实际上可以工作。vtable 仅存储指向函数的指针,因此即使在这种情况下,函数也是固定的。
由编译器决定在类对象中实际分配哪些部分,这些通常是非常量非静态成员变量,因此您可以将实际对象视为所有变量中存在的所有变量的简单结构子类和你的主类。