每个对象都需要有一个唯一的内存地址,以便可以在内存中定位它。如果一个空类型的对象不占用任何空间,那么在内存中就无法为不同的对象分配不同的地址,这会导致无法区分不同的对象实例。如果空类型的对象不占用任何空间,那么空类型对象的指针将无法与其他类型对象的指针兼容。在C++中,所有类型的指针大小都是相同的(在大多数平台上是4字节或8字节),这样可以保证指针运算和类型转换的一致性。如果允许空类型对象不占用任何空间,那么在内存中对齐(alignment)将会变得复杂。内存对齐是为了提高内存访问效率,如果一个对象不占用空间,它可能会导致内存中的其他对象无法按照它们所需的对齐方式来布局。如果数组元素类型为空类型,并且不占用空间,那么数组索引将失去意义,因为无法根据索引来访问数组元素。C++标准规定,即使是最简单的空类也至少占用一个字节的存储空间。这个字节通常不包含任何数据,但它确保了上述问题的解决。

比如:

class Empty {};

int main() {

Empty e1, e2;

std::cout << sizeof(Empty) << std::endl; // 通常输出 1

std::cout << &e1 << " " << &e2 << std::endl; // 输出不同的地址

return 0;

}

在这个例子中,sizeof(Empty)将返回1,表示Empty类型的对象至少占用一个字节的空间。同时,&e1和&e2将输出不同的地址,表明每个对象都有其唯一的内存位置。

在C++中,类的成员属性(成员变量)在类的构造函数中赋值时,通常是通过直接赋值或者通过构造函数初始化列表来完成的。当你在构造函数中给成员属性赋值时,是否调用构造函数取决于成员属性的类型。

具体情况:

对于内置类型(如 int, float, char 等)。当你给内置类型的成员属性赋值时,不会调用构造函数,因为内置类型没有构造函数。你只是在内存中直接存储一个值。

class MyClass {

public:

MyClass() {

value = 10; // 直接赋值,不调用构造函数

}

private:

int value;

};

对于自定义类型(即类类型)。当你给一个类类型的成员属性赋值时,如果你是在构造函数的初始化列表中进行的,那么会调用该类类型的构造函数。

class AnotherClass {

public:

AnotherClass(int val) {

// AnotherClass 的构造函数被调用

}

};

class MyClass {

public:

MyClass() : anotherClass(10) { // 在初始化列表中调用 AnotherClass 的构造函数

}

private:

AnotherClass anotherClass;

};

对于成员属性的赋值操作,如果你是在构造函数体内对类类型的成员属性进行赋值(而不是在初始化列表中),那么会调用该类类型的赋值运算符(如果有的话),而不是构造函数。

class AnotherClass {

public:

AnotherClass() {

// AnotherClass 的构造函数被调用

}

AnotherClass& operator=(const AnotherClass& other) {

// 赋值运算符被调用

return *this;

}

};

class MyClass {

public:

MyClass() {

anotherClass = AnotherClass(); // 调用 AnotherClass 的赋值运算符

}

private:

AnotherClass anotherClass;

};

在上面的例子中,anotherClass 的构造函数首先被调用以初始化 anotherClass,然后赋值运算符被调用以将新创建的临时 AnotherClass 对象赋值给 anotherClass

总结来说,是否调用构造函数取决于成员属性的类型以及赋值是在构造函数的初始化列表中进行的还是在函数体内进行的。对于内置类型,没有构造函数可调用;对于类类型,初始化列表会调用构造函数,而在函数体内的赋值操作会调用赋值运算符。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐