文章目录
- 前言
- C++ 类 & 对象
- 类成员函数
- C++ 类访问修饰符
- 公有(public)成员
- 私有(private)成员
- 受保护(protected)成员
- 继承中的特点
- 类的构造函数和析构函数
- C++友元函数
- C++内联函数
- C++ this指针
- C++指向类的指针
- C++ 类的静态成员
- C++拷贝构造函数
前言
事情的起因是复现一道羊城杯2023的vm_wo,题目中的虚假控制流,虽然解那道题可以不用去除,但是遇到了不学就很难受。好的学一下虚假控制流,那都看虚假控制流了;OLLVM总要一起看了吧,那都已经学到这里不能只看个皮毛吧;学一下实现原理吧,研究一下源码吧;好的就研究一下源码,打开来好嘟看不懂源码,C++和llvm啊。那不就只可以学一下C++了o(╥﹏╥)o,学吧学吧都得学。
因为有C基础,so只是总结一下学习记录。
C++ 类 & 对象
C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。
类用于指定对象的形式,是一种用户自定义的数据类型,它是一种封装了数据和函数的组合。类中的数据称为成员变量,函数称为成员函数。类可以被看作是一种模板,可以用来创建具有相同属性和行为的多个对象。
C++关键字class和对象定义:
/*
C++ 类定义
关键字class,定义一个类本质上是定义一个数据类型的蓝图。
它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。
*/
class Box
{
public: // 关键字 public 确定了类成员的访问属性。
double length;
double breadth;
double height;
};
/*
C++ 对象定义
类提供了对象的蓝图,所以对象基本是根据类来创建的。
*/
Box Box1; // 声明Box1,类型为Box
Box Box2; // 声明Box1,类型为Box
// 对象Box1 和 Box2 都有他们各自的数据成员。
实例:
#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;
}
类成员函数
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。
让我们看看之前定义的类 Box,现在我们要使用成员函数来访问类的成员,而不是直接访问这些类的成员;
成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义。在类定义中定义的成员函数把函数声明为内联的,即便没有使用 inline 标识符。所以您可以按照如下方式定义 getVolume() 函数。
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
double getVolume(void)
{
return length * breadth * height;
}
/*
double getVolume(void);// 返回体积 也可以这样写
*/
};
关于范围解析运算符 ::
范围解析操作符(也可称作Paamayim Nekudotayim)或者更简单地说是一对冒号,可以用于访问静态成员,类常量,还可以用于覆盖类中的属性和方法。 当在类定义之外引用到这些项目时,要使用类名。
您也可以在类的外部使用范围解析运算符 :: 定义该函数,如下所示:
double Box::getVolume(void)
{
return length * breadth * height;
}
C++ 类访问修饰符
数据封装是面向对象编程的一个重要特点,它防止函数直接访问类类型的内部成员。类成员的访问限制是通过在类主体内部对各个区域标记 public、private、protected 来指定的。关键字 public、private、protected 称为访问修饰符。
一个类可以有多个 public、protected 或 private 标记区域。每个标记区域在下一个标记区域开始之前或者在遇到类主体结束右括号之前都是有效的。成员和类的默认访问修饰符是 private。
公有(public)成员
公有成员在程序中类的外部是可以访问的,可以不适用任何成员函数来设置和获取公有变量的值。
#include <iostream>
using namespace std;
class Line
{
public:
double length;
void setLength( double len );
double getLength( void );
};
// 成员函数定义
double Line::getLength(void)
{
return length ;
}
void Line::setLength( double len )
{
length = len;
}
// 程序的主函数
int main( )
{
Line line;
// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
// 不使用成员函数设置长度
line.length = 10.0; // OK: 因为 length 是公有的
cout << "Length of line : " << line.length <<endl;
return 0;
}
// 运行结果
// Length of line : 6
// Length of line : 10
私有(private)成员
私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
实例:
class Box
{ ...
private:
double width;
};
...
// 程序的主函数
int main( )
{
Box box;
// 不使用成员函数设置长度
box.length = 10.0; // OK: 因为 length 是公有的
cout << "Length of box : " << box.length <<endl;
// 不使用成员函数设置宽度
// box.width = 10.0; // Error: 因为 width 是私有的
box.setWidth(10.0); // 使用成员函数设置宽度
cout << "Width of box : " << box.getWidth() <<endl;
return 0;
}
// 执行结果:
// Length of box : 10
// Width of box : 10
受保护(protected)成员
protected(受保护)成员变量或函数与私有成员十分相似,但有一点不同,protected(受保护)成员在派生类(即子类)中是可访问的。
#include <iostream>
using namespace std;
class Box
{
protected:
double width;
};
class SmallBox:Box // SmallBox 是派生类
{
public:
void setSmallWidth( double wid );
double getSmallWidth( void );
};
// 子类的成员函数
double SmallBox::getSmallWidth(void)
{
return width ;
}
void SmallBox::setSmallWidth( double wid )
{
width = wid;
}
// 程序的主函数
int main( )
{
SmallBox box;
// 使用成员函数设置宽度
box.setSmallWidth(5.0);
cout << "Width of box : "<< box.getSmallWidth() << endl;
return 0;
}
执行结果:
Width of box : 5
继承中的特点
如果在使用派生类 例如 先class A,定义了一个A数据类型。
再
- class B : public A
- class B : protected A
- class B : private A
可以选择继承的方式,原本在A中的各种成员到了B中 成员类型灰发生改变。
类的构造函数和析构函数
- 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。 - 类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
构造函数可以添加参数 ;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数
// Line(double len);
private:
double length;
};
// 定义构造函数
Line::Line( double len)
{
cout << "Object is being created, length = " << len << endl;
length = len;
}
使用效果就是,当对象创建时,会先输出Object is being created, length =
C++友元函数
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:
friend void printWidth( Box box );
声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:
friend class ClassTwo;
实例:
#include <iostream>
using namespace std;
class Box
{
double width;
public:
friend void printWidth(Box box); // 声明友元函数
void setWidth(double wid);
};
// 成员函数定义
void Box::setWidth( double wid)
{
width = wid;
}
// 请注意:printWidth()不是任何类的成员函数
void printWidth(Box box)
{
// 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员
cout << "Width of box : " << box.width << endl;
}
// 程序主函数
int main()
{
Box box;
// 使用成员函数设置宽度
box.setWidth(10.0);
//使用友元函数输出高度
printWidth(box);
return 0;
}
执行结果:
Width of box : 10
C++内联函数
内联函数是一种特殊类型的函数,它在被调用时会在调用点被展开,而不是像普通函数一样通过函数调用的方式执行。这意味着函数体的代码会被直接插入到调用该函数的地方,从而减少了函数调用的开销。
内联函数 用 关键字 inline 来声明
// 内联函数的定义
inline int add(int a, int b) {
return a + b;
}
需要注意的地方:
- 内联函数是插入代码,因此不应该随意内联大型函数
- 内联函数的声明和定义通常都应该放在头文件中,以便在多个源文件中使用。
- 内联函数修改后,可能需要重新编译所有使用该函数的源文件
C++ this指针
C++中的this指针是一个隐式指针,它指向当前对象的地址。它是每个非静态成员函数的一个隐式参数,用于访问调用该函数的对象的成员变量和成员函数。
class MyClass {
public:
int data;
void setData(int value) {
this->data = value; // 使用this指针来访问成员变量
}
void printData() {
cout << "Data: " << this->data << endl;
}
};
对于隐式使用:如上例,通常情况下,无需显式的使用this指针,它会自动隐式掉,例如“data = value”和“this->data = value;”具有同样的使用。
同时。友元函数没有 this 指针,因为友元函数不是类的成员,只有成员函数才有 this 指针。
C++指向类的指针
一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。
#include <iostream>
using namespace std;
class Box
{
public:
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
Box *ptrBox; // Declare pointer to a class.
// 保存第一个对象的地址
ptrBox = &Box1;
// 现在尝试使用成员访问运算符来访问成员
cout << "Volume of Box1: " << ptrBox->Volume() << endl;
// 保存第二个对象的地址
ptrBox = &Box2;
// 现在尝试使用成员访问运算符来访问成员
cout << "Volume of Box2: " << ptrBox->Volume() << endl;
return 0;
}
C++ 类的静态成员
我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。
声明:
class MyClass {
public:
static int staticVar; // 静态成员变量的声明
static void staticFunction(); // 静态成员函数的声明
};
// 静态成员变量的定义和初始化
int MyClass::staticVar = 0;
// 静态成员函数的定义
void MyClass::staticFunction() {
// 静态函数的实现
}
可以用类名或者对象实例来访问静态成员:
MyClass::staticVar = 42; // 使用类名访问静态成员变量
MyClass obj;
obj.staticVar = 23; // 使用对象实例访问静态成员变量
MyClass::staticFunction(); // 使用类名访问静态成员函数
obj.staticFunction(); // 也可以使用对象实例访问静态成员函数
静态成员是与类相关而不是与对象实例相关的成员,它们在整个类中共享,并且可以通过类名或对象实例来访问。
如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。
静态成员函数与普通成员函数的区别:
- 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
- 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
C++拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它用于创建一个对象的副本。
使用情况:
- 当使用一个对象初始化另一个对象时,会调用拷贝构造函数。
- 当对象作为函数参数传递给函数时,也会调用拷贝构造函数。
- 当从函数返回对象时,同样会调用拷贝构造函数。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:
classname (const classname &obj) {
// 构造函数的主体
}
实例:
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 简单的构造函数
Line( const Line &obj); // 拷贝构造函数
~Line(); // 析构函数
private:
int *ptr;
};
// 成员函数定义,包括构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}
Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函数
int main( )
{
Line line(10);
display(line);
return 0;
}