当我们在 C++ 中创建一个类的对象时调用了哪些方法,或者当我们创建一个类的对象时究竟会发生什么。
6 回答
如果没有其他信息,您应该假设调用了类本身的一个且只有一个成员函数,即构造函数。
class CheesePizza {
public:
CheesePizza() {}
};
CheesePizza barely; // will initialize by calling Foo::foo().
在许多情况下,可能会调用其他函数,例如,如果您创建必须创建临时对象的转换场景,例如指定 std::string 参数并传递 const char* 值。
class DIYPizza {
std::string m_toppings;
public:
DIYPizza(const std::string& toppings) : m_toppings(toppings) {}
};
DIYPizza dinner("all of the toppings");
这必须从 const char* 参数“所有的浇头”创建一个临时的 std::string。然后 DIYPizza::DIYPizza(std::string) 运算符可以传递这个临时(右值),然后我们从它初始化 m_str,它调用 std::string(const std::string&) 复制构造函数来初始化 m_str。
基本上完成的是:
// create a std::string object initialized with the c-string.
std::string rvalueString = std::string("all of the toppings");
// now we have a string to pass to DIYPizza's constructor.
DIYPizza dinner(rvalueString);
然而——此时我们仍然只调用Foo 的一个成员函数。调用更多类成员的唯一方法是从调用的构造函数中调用它们。
class DinnerAtHome {
void orderPizza();
void drinkBeer();
void watchTV(); // I was going with something more brazen, but...
public:
DinnerAtHome() {
orderPizza();
drinkBeer();
watchTV();
drinkBeer();
// arrival of pizza is handled elsewhere.
}
};
现在,当您构造一个新的 DinnerAtHome 时,将调用 5 个成员函数:DinnerAtHome::DinnerAtHome() 和它进行的四个成员函数调用。
注意:“new”不是成员函数,它是一个全局操作符。
如果答案是“五”,那么要么是面试官使用了一个技巧问题,要么你错过了所问问题的一些细微差别。
好的,我尝试过大的答案,因为没有一次已经选择了答案:当您创建一个对象时调用构造函数,这还涉及对成员对象的底层构造函数的调用,如果该对象继承自另一个对象,您还需要调用基的构造函数班级
class Foo: public Bar{
std::vector<int> indices; //default constructor for indices is called
public:
Foo(int a):Bar(a){ //you have to call constructor of Bar here
//else compiling error will occur
}
};
对于拥有它的对象,默认构造函数会被隐式调用。当您使用“new”创建内容时,首先分配内存,然后在该内存之上构造对象(放置 new 以类似的方式工作,但内存块可能由用户在其他地方显式创建)。
请注意,在某些情况下,您不知道构造函数调用的顺序:
情况1
Obj1* myobj= new Obj1 ( new Obj2, new Obj3( new Obj4) );
在这种情况下,您不知道 Obj4 是在 Obj2 之前还是之后创建的,如果 Obj3 是在 Obj2 之前或之后创建的,编译器将尝试选择“一个顺序”(这可能因编译器和平台而异),这也是一个高度不安全的代码:假设您在 Obj2 已经创建时在 Obj3 中遇到异常,那么 Obj3 内存将永远不会被释放(在这种极端情况下,您可能会找到有用的工具来解决这些问题:Hypodermic / Infectorpp)
案例二
静态/全局变量可能以不同的顺序初始化,您必须依赖特定的编译器函数来声明初始化顺序
对于海湾合作委员会:
void f __attribute__((constructor (N)));
另请注意,“构造函数”不像其他“方法”,实际上您不能将构造函数保存在 std::function 中,也不能将其与 std::bind 绑定。保存构造函数的唯一方法是用工厂方法包装它。
嗯,这应该是你在采访中提到的 5 件“事情”。
如果您正在创建一个类,则意味着您正在调用该类的构造函数。如果构造函数不在您的代码中,则编译器将添加默认构造函数。您可以覆盖默认构造函数来执行一些对象创建任务。例如:如果您正在创建 Person 类型的对象
Person* objPerson = new Person();
这意味着您正在调用该类的 Person() 方法(构造函数)。
class Person {
public:
Person() {
// ...
}
};
当构造一个新对象时,调用该类的构造函数来初始化该类的非静态成员变量(如果该类没有任何构造函数,编译器为该类分配一个空的构造函数并调用它)。构造函数可以调用其他函数(库或用户定义的函数),但这取决于程序员在构造函数中调用的函数。此外,如果类是继承层次结构的一部分,则编译器会在实例化类的构造函数之前调用基类的适当构造函数(请访问此处以获得简要说明)。
当类的对象被实例化时:类对象所需的内存(取决于其数据成员)在堆上分配,并且对该内存位置的引用由对象名称存储。根据调用的构造函数,堆上此内存位置的这些单独的数据成员可能会或可能不会被初始化。
让我们假设您的课程名为 CMyClass。
#include "MyClass.h"
/* GLOBAL objects */
static CMyClass g_obj("foo"); // static makes this object global to this file. you can use it
// anywhere in this file. the object resides in the data segment
// (not the heap or the stack). a string object is created prior
// to the constructor call.
int main(void)
{
....
if (theWorldEnds)
{
/* STACK */
CMyClass obj1; // this calls the constructor CMyClass(). It resides on the stack.
// this object will not exist beyond this "if" section
CMyClass obj2("MyName"); // if you have a constructor CMyClass(string&), the compiler
// constructs an object with the string "MyName" before it calls
// your constructor. so the functions called depend on which
// signature of the constructor is called.
/* HEAP */
CMyClass *obj3 = new CMyClass(); // the object resides on the heap. the pointer obj3
// doesn't exist beyond the scope of the "if" section,
// but the object does exist! if you lose the pointer,
// you'll end up with a memory leak. "new" will call
// functions to allocate memory.
}
}