首页 > 编程语言 >C++面向对象整理(1)之初识类和对象

C++面向对象整理(1)之初识类和对象

时间:2024-03-20 16:33:41浏览次数:25  
标签:const 函数 Point 对象 double 成员 C++ 面向对象 初识

C++面向对象整理(1)之初识类和对象

注:整理一些突然学到的C++知识,随时mark一下
例如:忘记的关键字用法,新关键字,新数据结构


C++ 的 类和对象


提示:本文为 C++ 中 类定义、成员函数 的写法和举例


一、类的定义

  类(class)就是自定义一种数据结构,这个数据结构就是更高级版本的C的结构体struct,拥有很多属性数据,但更一步相比结构体,类还新增了很多函数(称为成员函数、或方法),这些函数可以把这些属性数据当成传递的参数施加运算(如初始化赋值)或操作,而且还可以传新的参数,同时这些函数可以通过访问权限控制是否可以传入/访问这些数据。
在C++中,定义类(class)的基本语法如下:

class ClassName {  
     访问权限:
              类内给出类的成员变量的声明  
     访问权限:
     		  类内给出类的成员函数的声明或实现  
};

//类外定义成员函数,使用冒号
返回类型 ClassName::成员函数的实现

注意工程上一般将类分文件编写定义,h文件中写类的声明,源文件中写类的函数的具体实现。当然也可以一次性写在一个文件里。

类的成员函数可以分为以下几种类型:

(1)普通成员函数:这是最常见的成员函数类型,它们执行类的特定操作,并且通常依赖于类的特定实例(对象)来执行。

(2)构造函数:构造函数是一种特殊的成员函数,它在创建类的对象时被自动调用,用于初始化对象的成员变量。构造函数的名字必须与类名相同,并且不能有返回类型。构造函数可以重载,可以有多个构造函数,以应对不同的初始化需求(这个非常重要,这里后面讲)。

(3)析构函数:析构函数也是特殊的成员函数,它在对象的生命周期结束时被调用,用于执行清理工作、释放分配的内存等。析构函数的名字是类名前加上波浪号 ~。

(3)赋值操作符重载函数:这是重载的赋值操作符就是等于号 = ,用于实现对象之间的赋值操作。(后面讲)

(4)取地址操作符重载函数和const修饰的取地址操作符重载函数:这些是重载的取地址操作符(&),分别用于获取对象的地址和const对象的地址。

(5)virtual的成员函数:其地址指向vtable(虚函数表)中的位置,通常用于实现多态。

(6)static的成员函数:不依赖于类的对象而存在的函数,也不依赖于类的对象而调用。关于static和const
(7)const的成员函数:表示该函数不能擅自改变成员变量的值。

1、类成员的访问权限

类的访问权限有三种:

public:公开的,表示这个成员可以在任何地方被访问。
protected:受保护的,表示这个成员可以在该类其派生类中被其访问,但不能被该类的对象通过点运算符访问。
private:私有的,表示这个成员只能在类的内部被访问,类的外部无法直接访问。如果没有明确指定访问权限,那么默认是private

2、类定义示例

现在,我们来定义一个坐标点类Point,包含空间三维坐标(x, y, z)并提供提取GET或设置SET坐标值和一个求距离的接口distanceTo(方法),假设定义的距离是两点之间的欧几里得距离。

(1)类内定义

#include <cmath> // 用于sqrt函数 

class Point {  
public:  
	Point(double x, double y, double z) {  
    // 这里构造函数直接赋值  
    x_ = x;  
    y_ = y;  
    z_ = z;  
}  
   
    // 获取x坐标  
    double getX() const { return x_; }  
  
    // 获取y坐标  
    double getY() const { return y_; }  
  
    // 获取z坐标  
    double getZ() const { return z_; }  
  
    // 设置x坐标  
    void setX(double x) { x_ = x; }  
  
    // 设置y坐标  
    void setY(double y) { y_ = y; }  
  
    // 设置z坐标  
    void setZ(double z) { z_ = z; }  
  
    // 计算两点之间的欧几里得距离  
    double distanceTo(const Point& other) const {  
        double dx = x_ - other.x_;  
        double dy = y_ - other.y_;  
        double dz = z_ - other.z_;  
        return std::sqrt(dx * dx + dy * dy + dz * dz);  
    }  
  
private:  
    // 坐标变量  
    double x_;  
    double y_;  
    double z_;  
};

构造函数也可以写成以下初始化列表形式,也可用于用于初始化

Point(double x = 0.0, double y = 0.0, double z = 0.0)   
    : x_(x), y_(y), z_(z) {}  

(2)类外定义成员函数

比如我们要将Point类分为头文件(.h)和源文件(.cpp)分开编写,类外定义不一定要分开写,只是工程上一般这样写。

首先是头文件Point.h:

// Point.h  
#ifndef POINT_H  
#define POINT_H  
  
#include <cmath> // 包含cmath库以使用std::sqrt  
  
class Point {  
public:  
    // 构造函数,用于初始化坐标  
    Point(double x = 0.0, double y = 0.0, double z = 0.0);  
  
    // 获取x坐标  
    double getX() const;  
  
    // 获取y坐标  
    double getY() const;  
  
    // 获取z坐标  
    double getZ() const;  
  
    // 设置x坐标  
    void setX(double x);  
  
    // 设置y坐标  
    void setY(double y);  
  
    // 设置z坐标  
    void setZ(double z);  
  
    // 计算两点之间的欧几里得距离  
    double distanceTo(const Point& other) const;  
  
private:  
    // 坐标变量  
    double x_;  
    double y_;  
    double z_;  
};  
  
#endif // POINT_H

然后是源文件Point.cpp:

// Point.cpp  
#include "Point.h"  
  
// 构造函数实现  
Point::Point(double x = 0, double y = 0, double z = 0) {  
    // 这里直接赋值  
    x_ = x;  
    y_ = y;  
    z_ = z;  
}  
  
// 获取x坐标  
double Point::getX() const {  
    return x_;  
}  
  
// 获取y坐标  
double Point::getY() const {  
    return y_;  
}  
  
// 获取z坐标  
double Point::getZ() const {  
    return z_;  
}  
  
// 设置x坐标  
void Point::setX(double x) {  
    x_ = x;  
}  
  
// 设置y坐标  
void Point::setY(double y) {  
    y_ = y;  
}  
  
// 设置z坐标  
void Point::setZ(double z) {  
    z_ = z;  
}  
  
// 计算两点之间的欧几里得距离  
double Point::distanceTo(const Point& other) const {  
    double dx = x_ - other.x_;  
    double dy = y_ - other.y_;  
    double dz = z_ - other.z_;  
    return std::sqrt(dx * dx + dy * dy + dz * dz);  
}

在这个Point类中,我们定义了:

一个构造函数,用于初始化坐标值。
getX(), getY(), getZ()方法,用于获取坐标值。
setX(), setY(), setZ()方法,用于设置坐标值。
distanceTo()方法,用于计算当前点到另一个点的欧几里得距离。
x_, y_, z_是私有成员变量,只能在Point类内部访问,而公开的getX(), getY(), getZ(), setX(), setY(), setZ()方法提供了对私有成员变量的访问接口。distanceTo()方法使用了私有成员变量来计算距离。这样定义的类既保证了成员变量的封装性(只有类内部才能直接访问),又提供了公共接口供外部使用。

3、类对象(实例)的定义及初始化

有了自己定义好的class类型,我们就可以用它声明和初始化属于这个类的对象变量(实例 instance),比如,对于内置的数据类型有 int a = 1; int a; int b = a; 那么类该如何对应的实现上述初始化方式呢:
基于上面的类class Point来说明
于是:
(1)对应int a;

Point p1; 

执行这段代码时会立即调用定义的构造函数创建一个Point对象,坐标默认为(0, 0, 0)。

(1)对应int a = 1;

因为类有很多数据成员,但又不可能像python写作Point A ={1,2,3} 这么简单(好吧其实后面C++也引入了花括号的写法),因为编译器压根不知这是啥,所以必须引入构造函数来给每个数据成员初始化赋值:

Point p2(1.0, 2.0, 3.0); // 使用带参数的构造函数创建一个Point对象,坐标为(1.0, 2.0, 3.0)

(2)对应int b = a;

Point p3(p2); // 使用拷贝构造函数创建一个Point对象,坐标也为(1.0, 2.0, 3.0)
或者
Point p3 = p2; //若有重载的等号赋值

关于拷贝构造和重载运算符这里先不讲,记住这个语法形式即可。

(3)对应 int *p = & a;

Point * pointerP = & p3

但一般使用new关键字或智能指针在堆上动态创建对象(关于这两个详见前面关于动态内存new关于智能指针

Point* pointerP = new Point(7.0, 8.0, 9.0); // 在堆上动态创建一个Point对象,并返回指向它的指针

4、类的匿名对象

匿名对象(也称为临时对象)是指没有显式名称的对象。对于类来说,这意味着创建了一个类的对象,但没有给它一个变量名来用它。匿名对象通常用于那些只需要执行一次操作,如执行一次某个成员函数,并且之后不再需要引用的情况。匿名对象会在执行完构造函数或者调用成员函数后立即被销毁。一个类A的匿名对象的格式是A()或A(paras)

例如,上面的Point类,我只想执行一次计算距离函数来得到两个点的距离dist

double dist = Point(-1,-2,-3).distanceTo(p2);//Point(-1,-2,-3)就是匿名对象

再比如有一个简单的类,它有一个输出消息的成员函数:

class MessagePrinter {
public:
    void printMessage(const std::string& msg) const {
        std::cout << msg << std::endl;
    }
};

可以创建一个匿名对象并立即调用其成员函数,如下所示:

MessagePrinter().printMessage("Hello, World!");

本例MessagePrinter() 创建了一个 MessagePrinter 类的匿名对象,并立即调用了其 printMessage 成员函数。由于这个对象没有名称,因此它只存在于这次函数调用期间。当 printMessage 函数执行完毕后,这个匿名对象就会被销毁

5、类对象的内存分配

成员变量和成员函数在内存中的存储和访问方式有着根本的区别。在内存中,一个类的对象通常按照成员变量的声明顺序进行布局。成员变量的内存布局可能受到内存对齐规则的影响,以确保访问效率。而成员函数则不直接占用对象的内存空间,它们的代码在程序的代码段中成员变量是每个对象特有的,而成员函数则是类共有的。

成员变量
成员变量是类定义中的一部分,它们存储在类的对象中。当你创建一个类的对象时,系统会在内存中为对象的每个成员变量分配空间。这些空间通常位于堆(如果使用new操作符)或栈(如果在函数内部创建对象)上。每个对象都有自己的成员变量副本,因此修改一个对象的成员变量不会影响其他对象的成员变量,除非这些对象之间通过引用或指针相互关联。

成员函数
成员函数也是类定义中的另一部分,但它们并不直接存储在类的对象中。相反,成员函数通常存储在代码段中,这是程序的可执行部分。每个成员函数只有一个代码实例,无论创建了多少个类的对象,只有一块内存成员函数。当对象调用成员函数时,函数代码被执行,并且函数的参数(如果有的话)以及类的this指针(指向调用该函数的对象的指针)被传递给函数。

this指针(后面讲)
在成员函数内部,可以通过this指针访问调用该函数的对象的成员变量。this指针是一个隐含的指针,它指向调用成员函数的对象。通过this指针,成员函数可以访问和修改对象的成员变量。

静态成员
静态成员变量和静态成员函数是类的特殊成员。静态成员变量不属于任何特定的对象实例,而是属于类本身。它们只存储一份,而不是每个对象一份。静态成员函数也是属于类本身的,它们不能访问非静态成员变量(除非通过对象或引用),但可以直接访问静态成员变量和其他静态成员函数。

标签:const,函数,Point,对象,double,成员,C++,面向对象,初识
From: https://blog.csdn.net/ULTRAmanTAROACE/article/details/136846086

相关文章

  • C++ static函数调用问题
    静态成员变量虽然在类中,但它并不是随对象的建立而分配空间的,也不是随对象的撤销而释放(一般的成员在对象建立时会分配空间,在对象撤销时会释放)。静态成员变量是在程序编译时分配空间,而在程序结束时释放空间。静态成员的定义和声明要加个关键static。静态成员可以通过双冒号来使用......
  • C++ 重载运算符返回值问题
    事实上,我们的重载运算符返回void、返回对象本身、返回对象引用都是可以的,并不是说一定要返回一个引用,只不过在不同的情况下需要不同的返回值。那么什么情况下要返回对象的引用呢?原因有两个:允许进行连续赋值;防止返回对象(返回对象也可以进行连续赋值(常规的情况,如a=b=c,而不......
  • 开发之单元测试—Test_C++的gtest单元测试
    单元测试测试的本质其实都是一样的,都是通过给定参数来执行函数,然后判断函数的实际输出结果和期望输出结果是否一致测试框架gtest采用的是xUnit架构,JUnitPyUnit:PyUnit主要用于进行白盒测试和回归测试C++测试gtest单元测试是Google的一套用于编写C++测试的框架,可以运......
  • C++单例基类
    在C++中实现单例模式可以使用模板和C++11的特性来达到目的。下面是一个简单的示例代码:#include<iostream>template<typenameT>classSingleton{public:staticT&getInstance(){staticTinstance;returninstance;}Singleton(const......
  • 线程同步 SynchronizationContext 初识
    什么是SynchronizationContext?SynchronizationContext是.NET中的一个类,用于管理跨线程的同步操作。它提供了一种机制,使线程可以协调对共享资源的访问,从而防止并发问题。SynchronizationContext的工作原理SynchronizationContext与每个线程相关联。当线程执行时,它会使用关......
  • C++单例基类
    要实现一个安全的C++单例基类,确保子类不会随便覆盖单例行为,我们可以使用一种技巧,即CRTP(CuriouslyRecurringTemplatePattern)。这种模式使得基类能够访问派生类的私有和保护成员,从而允许我们在基类中实现单例逻辑,并且保证派生类不会破坏这个逻辑。以下是一个使用CRTP实现的单例......
  • 美国政府敦促开发者:停止使用 C、C++
    “C、C++不安全,新应用开发时就别用了,旧应用应该采取迁移行动”,近日,美国白宫国家网络主任办公室(ONCD)在一份主题为《回到基础构件:通往安全软件之路》的19页PDF报告中强烈呼吁道。其直言,C和C++这几种编程语言既缺乏与内存安全相关的特性,又在关键系统中大量使用,可......
  • 【Java初阶(一)】初识Java
    ❣博主主页:33的博客❣▶文章专栏分类:Java从入门到精通◀......
  • C/C++开发问题总结
    1.结构体sizeof问题Linux支持指定结构体比特字段,Windows不支持指定结构体比特字段structindex{longlongstart=0;longlongend=0;unsignedlonglongix=0;shortsegment=0;unsignedshortt:2;unsignedlonglongseq:(64-18);......
  • C++ 函数重载
    一组函数,函数名相同。函数的参数类型或参数个数不同,那么这一组函数就称作函数重载。C++为什么支持函数重载?C++代码产生函数符号的时候,是由函数名+参数列表组成的;C代码产生函数符号的时候,是由函数名来决定;函数重载需要注意什么?一组函数如果是重载函数,一定是处于同一作用......