C++学习笔记_4_ 类与对象(1)
目录
面向对象和面向过程
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
一、类是什么?
C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
二、类的定义
在C++中,类的定义使用class关键字。下面是一个简单的C++类的定义示例:
#include <iostream>
#include <string>
// 定义一个名为Person的类
class Person {
public: // 访问修饰符,表示以下成员是公开的
// 构造函数
Person(const std::string& name, int age) : name_(name), age_(age) {}
// 析构函数
~Person() {
// 在这里通常做一些清理工作,但在这个简单的例子中我们不需要
}
// 成员函数(方法)
void sayHello() const {
std::cout << "Hello, my name is " << name_ << " and I am " << age_ << " years old." << std::endl;
}
// 访问器(getter)函数
std::string getName() const {
return name_;
}
int getAge() const {
return age_;
}
// 修改器(setter)函数(可选)
void setAge(int age) {
if (age >= 0) { // 添加一个简单的年龄验证
age_ = age;
}
}
private: // 访问修饰符,表示以下成员是私有的
std::string name_; // 成员变量(属性或字段)
int age_;
};
int main() {
// 创建Person类的实例(对象)
Person alice("Alice", 30);
// 调用成员函数
alice.sayHello();
// 访问成员变量(通过getter函数)
std::cout << "Name: " << alice.getName() << std::endl;
std::cout << "Age: " << alice.getAge() << std::endl;
// 修改年龄(通过setter函数)
alice.setAge(31);
std::cout << "Updated Age: " << alice.getAge() << std::endl;
return 0;
}
在上面的示例中:
- Person 是类的名称。
- public 和 private 是访问修饰符,它们决定了类成员的访问权限。public 成员可以在类的外部被访问,而 private 成员只能在类的内部被访问。
- 类的成员包括成员函数(方法)和成员变量(属性或字段)。在这个例子中,sayHello 是一个成员函数,而 name_ 和 age_ 是成员变量。
- 构造函数 Person(const std::string& name, int age) 用于初始化对象的状态。它通过成员初始化列表(: name_(name), age_(age))来设置成员变量的值。
- 析构函数 ~Person() 是一个特殊的成员函数,它在对象被销毁时自动调用。在这个例子中,析构函数是空的,因为类没有需要清理的资源。
- 成员函数 getName() 和 getAge() 是访问器(getter)函数,用于返回成员变量的值。
- 成员函数 setAge(int age) 是修改器(setter)函数,用于设置成员变量的值。在这个例子中,它还包括一个简单的年龄验证。
- 成员变量 name_ 和 age_ 是私有的,这意味着它们只能通过公共的访问器或修改器函数来访问或修改。这是封装的一个基本示例,它隐藏了类的内部实现细节,并提供了对类状态的受控访问。
类的两种定义方式:
- 声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
- 声明放在.h文件中,类的定义放在.cpp文件中。
三、类的访问限定符及封装
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
【访问限定符说明】
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- class的默认访问权限为private,struct为public(因为struct要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
**
重要问题:C++中struct和class的区别是什么?
**
解答:C++需要兼容C语言,所以C++中struct可以当成结构体去使用。另外C++中struct还可以用来定义类。
和class是定义类是一样的,区别是struct的成员默认访问方式是public,class是struct的成员默认访问方式是private。
四、类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。
在C++中,类的作用域(Scope)是指类定义中声明的名称(包括成员变量、成员函数、嵌套类型等)的可见性和访问权限的范围。当一个名称在类的作用域内声明时,它就可以在类的内部被访问,并可能根据访问修饰符(如public、protected、private)在类的外部被访问。
访问修饰符
C++提供了三种访问修饰符来控制类成员的访问权限:
- public:成员在类的内部和外部都是可访问的。
- protected:成员在类的内部和派生类中是可访问的,但在类的外部是不可访问的(除非通过派生类的对象)。
- private:成员只能在类的内部访问,在类的外部和派生类中都是不可访问的。
作用域解析运算符(::)
作用域解析运算符(::)用于指定类作用域中的名称。这在以下情况下特别有用:
- 当有多个类具有相同名称的成员时,可以使用::来区分它们。
- 当在类外部定义成员函数时,可以使用::来指定该函数属于哪个类。
class MyClass {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
void privateFunction() {
// 这里可以访问 publicVar, protectedVar, 和 privateVar
}
// 静态成员函数也可以访问类的所有成员(包括私有成员)
static void staticFunction() {
// 静态成员函数不能直接访问非静态成员,因为它们不绑定到类的实例
// 但可以通过对象或指针/引用来访问
}
};
int main() {
MyClass obj;
// 可以访问 publicVar
obj.publicVar = 10;
// 不能直接访问 protectedVar 和 privateVar
// obj.protectedVar = 20; // 错误
// obj.privateVar = 30; // 错误
// 静态成员函数也不能直接访问非静态成员
// MyClass::staticFunction(); // 如果该函数试图访问非静态成员,会编译错误
// 但在类的内部(成员函数内部)或友元函数/类中,可以访问所有成员
return 0;
}
嵌套类和枚举的作用域
类还可以包含嵌套类(在类内部定义的类)和枚举。这些嵌套类型和枚举的作用域是它们所属的外部类。要从外部访问这些嵌套类型或枚举,需要使用外部类的名称和::运算符。
class OuterClass {
public:
enum Color {
RED, GREEN, BLUE
};
class NestedClass {
public:
void doSomething() {
// 可以访问 OuterClass::Color
}
};
};
int main() {
OuterClass::Color color = OuterClass::RED; // 访问嵌套枚举
OuterClass::NestedClass nestedObj; // 创建嵌套类的对象
return 0;
}
继承与作用域
在继承中,派生类可以访问基类中的public和protected成员(但不能直接访问private成员),但这些成员的访问权限可能会根据派生类的访问修饰符而发生变化。在派生类的作用域中,派生类成员和基类成员共享同一个作用域,但派生类成员会覆盖(或隐藏)与其同名的基类成员。
五、类的实例化
用类类型创建对象的过程,称为类的实例化
- 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
- 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量。
- 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。
在C++中,类的实例化(Instantiation)是指创建一个类的对象(Object)的过程。对象是根据类的定义创建的内存中的具体存在,它包含了类所定义的属性和方法(在C++中称为成员函数)。通过实例化类,我们可以使用类的属性和方法。
下面是一个C++类实例化的示例:
#include <iostream>
#include <string>
// 定义一个名为Person的类
class Person {
public:
// 构造函数
Person(const std::string& name, int age) : name_(name), age_(age) {}
// 成员函数(方法)
void sayHello() const {
std::cout << "Hello, my name is " << name_ << " and I am " << age_ << " years old." << std::endl;
}
private:
std::string name_; // 成员变量(属性)
int age_;
};
int main() {
// 创建Person类的实例(对象)
Person alice("Alice", 30); // 实例化Person类,创建一个名为alice的对象
// 调用对象的成员函数
alice.sayHello(); // 输出:Hello, my name is Alice and I am 30 years old.
// 注意:不能直接访问对象的私有成员(如alice.name_或alice.age_),因为它们是私有的
return 0;
}
在上面的示例中,Person 类被定义并包含了一个构造函数和一个成员函数 sayHello。在 main 函数中,我们使用 Person 类的构造函数创建了一个名为 alice 的对象,并传递了 “Alice” 和 30 作为参数来初始化 alice 的 name_ 和 age_ 成员变量。接着,我们调用了 alice 对象的 sayHello 方法,该方法输出了对象的名字和年龄。
类的实例化是通过调用类的构造函数来完成的。在C++中,构造函数是一个特殊的成员函数,它在创建类的对象时自动被调用。构造函数的名称与类名相同,没有返回类型(连 void 也没有)。在这个例子中,Person 类的构造函数接受两个参数(一个 std::string 类型的 name 和一个 int 类型的 age),并使用成员初始化列表来初始化对象的成员变量。
实例化类时,会在内存中为对象分配空间,并根据构造函数的参数初始化对象的成员变量。然后,就可以通过该对象来访问其公有(public)或保护(protected)的成员函数和成员变量(如果提供了访问器函数)。