1. 如果类A是一个空类,那么sizeof(A)的值为多少?
在C++中,即使是一个空类(即一个没有任何数据成员和成员函数的类),sizeof
还是会返回一个大于0的值。这是因为,即便是空类,也需要有一种方式来识别其实例。因此,C++标准规定,空类的大小至少为1字节。
以代码为例:
class A { };
std::cout << sizeof(A) << std::endl;
//这段代码将会输出 1,意味着空类 A 的大小是1字节。
2.覆盖和重载之间有什么区别?
“覆盖”和”重载”在C++面向对象编程中是两个非常重要的概念,它们的区别如下:
- 函数重载(Overloading):这发生在同一个类中,当你有两个或更多数量的函数拥有相同的名字但是参数列表不同(比如参数数量不同,参数类型不同,或者参数顺序不同)时,我们就称之为函数重载。例如:
class Example {
public:
void func(int a) { /*...*/ } // 第一个版本的func函数
void func(double a) { /*...*/ } // 第二个版本的func函数,参数是双精度浮点数
};
- 函数覆盖(Overriding):这发生在继承关系中,当子类有一个和父类完全相同的函数(函数名相同,参数列表相同,返回类型也相同),我们就称之为函数覆盖。子类覆盖父类的函数是为了提供不同的实现,这是多态性的一个重要体现。例如:
class Base {
public:
virtual void func() { /*...*/ } // 父类的func函数
};
class Derived : public Base {
public:
void func() override { /*...*/ } // 子类的func函数,覆盖了父类的func函数
};
在这个例子中,如果你有一个指向Derived类对象的Base类指针,并且通过这个指针调用func
函数,将会执行Derived类的func
函数,这就是多态性的体现。
3.拷贝构造函数和赋值运算符重载之间有什么区别?
拷贝构造函数和赋值运算符重载都用于在C++中复制对象,但是它们的用途和执行方式有所不同。
- 拷贝构造函数:拷贝构造函数在一个新对象创建的时候被调用,用于将一个已存在的对象的状态(也就是成员变量的值)复制到新对象。例如:
class Example { public: int value; Example(int val) : value(val) { } // 常规构造函数 Example(const Example& other) : value(other.value) { } // 拷贝构造函数 }; Example a(10); // 使用常规构造函数 Example b(a); // 使用拷贝构造函数,b的value值将会是10
-
赋值运算符重载:赋值运算符重载在一个已经存在的对象需要被赋予另一个已经存在的对象的状态时被调用。例如:
class Example {
public:
int value;
Example(int val) : value(val) { } // 常规构造函数
Example& operator=(const Example& other) { // 赋值运算符重载
if (this != &other) { // 防止自我赋值
value = other.value;
}
return *this;
}
};
Example a(10); // 使用常规构造函数
Example b(20); // 使用常规构造函数
b = a; // 使用赋值运算符重载,b的value值将会是10
注意,赋值运算符重载通常需要注意自我赋值的情况,并且应返回对象本身的引用,这样可以支持连续赋值如a = b = c
。
总的来说,拷贝构造函数用于初始化新对象,而赋值运算符重载用于已存在的对象。
4.对虚函数和多态的理解
虚函数和多态是C++面向对象编程中非常重要的概念,它们之间有着密切的关联。
虚函数是在基类中声明的带有virtual
关键字的成员函数。它允许在派生类中进行函数的重定义,从而实现多态性。当基类指针或引用指向派生类对象,并调用虚函数时,实际执行的是派生类中重定义的函数。这种行为称为动态绑定或后期绑定。
以下是一个示例:
class Animal {
public:
virtual void makeSound() {
cout << "Animal makes a sound." << endl;
}
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Dog barks." << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Cat meows." << endl;
}
};
int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->makeSound(); // 输出:Dog barks.
animal2->makeSound(); // 输出:Cat meows.
delete animal1;
delete animal2;
return 0;
}
在上面的例子中,Animal
是基类,Dog
和Cat
是派生类。makeSound()
函数在基类中被声明为虚函数,并在派生类中进行了重定义。当使用基类指针指向派生类对象并调用makeSound()
函数时,根据实际对象的类型,将执行相应派生类中的函数。这就展现了多态性的特性。
多态性使得程序能够根据实际对象的类型来动态地调用相应的函数,提供了灵活性和可扩展性。通过使用虚函数和多态,我们可以编写更具通用性和可维护性的代码。
5.请你来说一下C++中struct和class的区别
在C++中,struct
和class
是用于定义类的关键字,它们之间的主要区别在于默认的访问权限和继承方式。
- 默认的访问权限:在
struct
中,默认的访问权限是public
,也就是说,struct
中的成员变量和成员函数默认是可以被外部访问的。而在class
中,默认的访问权限是private
,也就是说,class
中的成员变量和成员函数默认是只能在类的内部访问的。
下面是一个示例来说明这一点:
struct MyStruct {
int publicVariable; // 默认为public
private:
int privateVariable;
};
class MyClass {
int privateVariable; // 默认为private
public:
int publicVariable;
};
- 继承方式:在C++中,类可以通过继承来扩展其功能。对于
struct
来说,默认的继承方式是public
继承,而对于class
来说,默认的继承方式是private
继承。
下面是一个示例来说明这一点:
struct BaseStruct {
int x;
};
struct DerivedStruct : BaseStruct { // 默认为public继承
int y;
};
class BaseClass {
int x;
};
class DerivedClass : BaseClass { // 默认为private继承
int y;
};
除了上述区别之外,struct
和class
在其他方面是相似的,它们都可以拥有成员变量和成员函数,并且都可以用于定义对象。选择使用struct
还是class
取决于你对类的设计意图和数据封装的需求,以及个人或团队的编程风格习惯。