类和对象
文章目录
1.数据抽象
- 忽略一个主题中与当前问题无关的方面,以便更充分地注意与当前问题有关的方面
- 只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节
- 好处:
- 类的内部受到保护,不会因无意的用户级错误导致对象状态受损。
- 数据抽象有助于提高代码的可维护性和可复用性。它将数据的表示和操作进行了封装,使得用户只需关注接口的使用,而无需关心底层的实现。这样可以降低代码的耦合度,提高代码的灵活性和可扩展性。
// 抽象数据类型 - 矩形
class Rectangle {
private:
int length;
int width;
public:
void setLength(int len) {
length = len;
}
void setWidth(int wid) {
width = wid;
}
int getArea() {
return length * width;
}
};
Rectangle类表示一个矩形,它包含了矩形的长度和宽度。通过设置访问器函数(setLength和setWidth)和获取器函数(getArea),外部用户可以通过这些接口来操作矩形对象,而无需了解矩形对象内部是如何实现的。
2.类
定义:描述数据抽象的结果,实现对数据和函数的封装
类的成员函数
把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
- 成员函数可以定义在类定义内部。在类定义中定义的成员函数把函数声明为内联的,即便没有使用 inline 标识符。
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
double getVolume(void)
{
return length * breadth * height;
}
};
- 或者单独使用范围解析运算符 :: 来定义
- 在这里,需要强调一点,在 :: 运算符之前必须使用类名。调用成员函数是在对象上使用点运算符(.)
double Box::getVolume(void)
{
return length * breadth * height;
}
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
// 成员函数声明
double get(void);
void set( double len, double bre, double hei );
};
// 成员函数定义
double Box::get(void)
{
return length * breadth * height;
}
void Box::set( double len, double bre, double hei)
{
length = len;
breadth = bre;
height = hei;
}
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 用于存储体积
// box 1 详述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 详述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// box 1 的体积
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Box1 的体积:" << volume <<endl;
// box 2 的体积
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Box2 的体积:" << volume <<endl;
// box 3 详述
Box3.set(16.0, 8.0, 12.0);
volume = Box3.get();
cout << "Box3 的体积:" << volume <<endl;
return 0;
}
//需要注意的是,私有的成员和受保护的成员不能使用直接成员访问运算符 (.) 来直接访问
3.构造函数与析构函数
class CMyString
{
public:
//构造函数
CMyString(char* s)
{
str = new char[strlen(s) + 1];
strcpy(str, s);
}
//析构函数
~CMyString()
{
delete[] str;
}
private:
char* str;
};
构造函数(Constructor)
是一种特殊的成员函数,用于创建对象时进行初始化操作。它具有与类同名的特殊函数,没有返回类型(包括void),在对象创建时自动被调用。创建类的对象时,自动调用类的构造函数 。 创建一个对象时只调用一个构造函数(根据参数列表)且只在创建时调用一次。构造函数可以进行一些对象的默认初始化,分配内存空间,执行必要的设置等操作。它可以有多个重载形式,根据参数列表的不同,选择不同的构造函数来创建对象。
class MyClass {
public:
// 默认构造函数
MyClass() {
// 初始化操作
}
// 带参数的构造函数
MyClass(int value) {
// 初始化操作
}
};
重载构造函数
类的构造函数可以重载,重载的构造函数的函数名完全相同,都没有返回值类型,但参数列表各不相同,类有多个构造函数时,系统根据创建对象时提供的参数来确定调用哪个构造函数,创建一个对象时只会根据参数列表调用类的一个构造函数, 而且只调用一次,不会调用所有构造函数
默认构造函数
不需要任何参数的构造函数是默认构造函数,一个类不能同时拥有多个默认构造函数
复制构造函数
- 在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象
- 复制构造函数的参数是该类的对象,一般使用传引用的方式
#include<iostream>
using namespace std;
class Complex{
public:
double real, imag;
Complex(double r,double i){
real = r; imag = i;
}
Complex(const Complex & c){
real = c.real; imag = c.imag;
cout<<"Copy Constructor called"<<endl ;
}
};
int main(){
Complex cl(1, 2);
Complex c2 (cl); //调用复制构造函数
cout<<c2.real<<","<<c2.imag;
return 0;
}
#include<iostream >
using namespace std;
class Complex
{
public:
double real, imag;
Complex(double r, double i) {
real= r; imag = i;
}
};
int main(){
Complex cl(1, 2);
Complex c2 (cl); //用复制构造函数初始化c2
cout<<c2.real<<","<<c2.imag; //输出 1,2
return 0;
}
析构函数(Destructor)
是另一个特殊的成员函数,用于在对象销毁时进行清理工作。它具有与类同名的特殊函数,以波浪号(~)开头,没有参数和返回值,也不能带有任何参数。析构函数在对象被销毁时自动被调用,用于释放对象占用的资源,执行必要的收尾操作。
class MyClass {
public:
// 构造函数
MyClass() {
// 初始化操作
}
// 析构函数
~MyClass() {
// 清理操作
}
};
4.类的静态成员
-
关键字:static
-
声明方式:static + 数据或函数定义
-
静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。
-
静态成员函数只能访问类的静态成员,不能调用非静态成员函数和访问非静态数据成员
-
静态成员函数与普通成员函数的区别:
-
静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
-
普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
-
-
访问静态成员的方式:
- 对象名 + 点操作符 (.)
- 对象的引用 + 点操作符 (.)
- 类指针 + 箭头操作符 (->)
- 类名 + 作用域运算符(::)
5.头文件的包含问题
- 头文件会被其他头文件或源文件包含
- 一个源文件可能会多次包含一个头文件
- C++允许嵌套包含,但不允许递归包含
- #pragma once让头文件只被编译一次,作用对象是物理文件,由编译器防止被包含内容的重复定义
6.this指针
- 每个对象都有一个指向自身的this指针
- 对象调用成员函数时会将自己的this指针传递给成员函数 (隐含参数)
- this是一个隐藏的指针,可以在类的成员函数中使用,它可以用来指向调用对象
- 成员函数访问对象的所有数据成员都是通过this指针(基地址)访问
- 友元函数没有 this 指针,因为友元不是类的成员,只有成员函数才有 this 指针
class Test {
public:
Test(int n = 0) {
data = n;
}
void print() {
cout << "data = " << this->data << "!" << endl;
}
private:
int data;
};
-
类的静态成员函数为什么不能访问非静态数据成员?
- 静态成员函数(属于类)没有维护this指针
- 访问非静态数据成员(属于对象)需要this指针
-
静态成员函数为什么不能调用非静态成员函数
- 调用非静态成员函数需要一个隐含参数(this指针)
- 静态成员函数没有
7.友元函数和友元类
类的数据成员都设为私有的——良好的程序设计风格
如果某个(些)外部函数需要访问类的数据成员,怎么办?
- 方法一:数据公有
- 方法二:友元
友元函数
-
友元函数是外部函数
-
友元函数具有访问类的所有成员的权限 ,有权访问类的所有私有(private)成员和保护(protected)成员
-
声明友元函数的方法: friend + 函数原型
-
除非能带来极大的便利,否则不要使用友元
class Triangle { //声明setA是本类的友元函数 friend void setA(Triangle& t, int n); public: Triangle(int x = 5, int y = 5, int z = 5); void print(); private: int a, b, c; };
友元类
一个类的友元类的所有成员函数都有访问类的所有成员的权限
友元类的声明方法: friend + class +类名
class B;
class A
{
public:
void setB(B& b, int m);
void print(B& b);
};
class B
{
friend class A; //声明友元类
private:
int data;
};
void setA(Triangle& t, int n);
public:
Triangle(int x = 5, int y = 5, int z = 5);
void print();
private:
int a, b, c;
};
#### 友元类
一个类的友元类的所有成员函数都有访问类的所有成员的权限
友元类的声明方法: ==friend + class +类名==
```cpp
class B;
class A
{
public:
void setB(B& b, int m);
void print(B& b);
};
class B
{
friend class A; //声明友元类
private:
int data;
};