一、数据封装和成员访问权限
1、结构体(struct)
在struct中,默认的成员访问权限是public。这意味着,在结构体外部,我们可以直接访问其成员变量和成员函数。下面是一个简单的例子:
struct Point {
int x, y;
};
Point p;
p.x = 5;
p.y = 10;
2、类(class)
与struct不同,class的默认成员访问权限是private。这意味着,我们不能在类的外部直接访问其成员变量和成员函数。为了访问这些成员,我们需要使用public成员函数。下面是一个使用类的例子:
class Point {
private:
int x, y;
public:
// Getter methods
int getX() const { return x; }
int getY() const { return y; }
// Setter methods
void setX(int newX) { x = newX; }
void setY(int newY) { y = newY; }
};
Point p;
p.setX(5);
p.setY(10);
二、继承
当涉及到继承时,struct和class之间的另一个区别是它们的默认访问级别。在C++中,类可以从其他类或结构体继承,反之亦然。在struct中,继承默认访问权限是public。而在class中,继承默认访问权限是private。
struct Animal {
void eat() {
std::cout << "Animal is eating." << std::endl;
}
};
struct Dog : Animal {
void bark() {
std::cout << "Dog is barking." << std::endl;
}
};
class Cat : Animal {
void meow() {
std::cout << "Cat is meowing." << std::endl;
}
};
int main() {
Dog dog;
dog.eat(); // OK
dog.bark(); // OK
Cat cat;
cat.eat(); // ERROR: 'eat' is a private member of 'Animal'
cat.meow(); // ERROR: 'meow' is a private member of 'Cat'
return 0;
}
在上面的示例中,我们定义了一个Animal结构体和一个Dog结构体,以及一个Cat类。Dog继承自Animal,并添加了一个bark()函数。而Cat也继承自Animal,并添加了一个meow()函数。
在main()函数中,我们创建了一个Dog对象和一个Cat对象。由于Dog继承自Animal的成员默认是public访问权限,所以我们可以调用dog.eat()和dog.bark()函数。但由于Cat继承自Animal的成员默认是private访问权限,所以我们不能调用cat.eat()和cat.meow()函数。
三、对象大小
在C++中,对象的大小是很重要的,它决定了对象在内存中的大小。在struct和class中,对象的大小也有一些区别。
在struct中,对象的大小等于所有成员变量的大小之和。而在class中,对象的大小还要考虑虚函数表的大小。
虚函数表是一张表格,用于存储类中的虚函数指针。每个含有虚函数的类都有一个虚函数表。当对象被实例化时,它会分配一段内存来存储虚函数表。虚函数表的大小取决于类中的虚函数数量和编译器实现。
下面是一个示例:
struct Person {
std::string name;
int age;
};
class PersonWithVirtualFunction {
std::string name;
int age;
virtual void sayHello() {}
};
int main() {
std::cout << "Size of Person: " << sizeof(Person) << std::endl;
std::cout << "Size of PersonWithVirtualFunction: " << sizeof(PersonWithVirtualFunction) << std::endl;
return 0;
}
在上面的示例中,我们定义了一个Person结构体和一个PersonWithVirtualFunction类。Person只有两个成员变量,name和age。而PersonWithVirtualFunction也有两个成员变量,name和age,但还包含一个虚函数sayHello()。
在main()函数中,我们分别输出了Person和PersonWithVirtualFunction的大小。可以看到,Person的大小是12个字节(4个字节name,4个字节age和4个字节的填充字节),而PersonWithVirtualFunction的大小是16个字节(8个字节name和age,8个字节的虚函数指针)。
四、构造函数和析构函数
在C++中,构造函数和析构函数是用于对象的初始化和清理的特殊函数。它们在struct和class中的使用也有一些区别。
在struct中,如果没有显式定义构造函数和析构函数,编译器会自动生成默认的构造函数和析构函数。而在class中,如果没有显式定义构造函数和析构函数,编译器只会生成默认的构造函数,不会生成析构函数。
下面是一个示例:
struct Person {
std::string name;
int age;
};
class Car {
std::string brand;
int price;
public:
Car() {
std::cout << "Constructing Car object." << std::endl;
}
~Car() {
std::cout << "Destroying Car object." << std::endl;
}
};
int main() {
Person person; // OK, 编译器生成默认构造函数
Car car; // OK, 显式定义了构造函数和析构函数
return 0;
}
在上面的示例中,我们定义了一个Person结构体和一个Car类。Person没有显式定义构造函数和析构函数,所以编译器会自动生成默认的构造函数和析构函数。而Car显式定义了构造函数和析构函数。
在main()函数中,我们创建了一个Person对象和一个Car对象。由于Person没有显式定义构造函数和析构函数,所以编译器会自动生成默认的构造函数和析构函数。而Car显式定义了构造函数和析构函数,所以我们可以看到构造函数和析构函数的输出。
总结:
在本文中,我们详细探讨了struct和class的区别。结构体和类都可以用来定义一个类,可以包含成员变量和成员函数。它们的主要区别在于默认访问权限、成员函数、继承和访问控制、对象大小以及构造函数和析构函数。在选择使用结构体还是类时,应考虑这些差异以及项目的需求。通常,如果需要更强的数据封装和访问控制,选择使用类可能更合适;而对于简单的数据结构,结构体可能更易于理解和使用。