类
定义:
类用于指定对象的形式,它包含了数据和用于处理数据的方法,可以把类当作自定义的数据类型

访问权限:
默认访问权限private
**public**公有权限:一个类的public成员变量、成员函数,可以通过类的成员函数、类的实例变量进行访问
**protected**保护权限:一个类的protected成员变量、成员函数,无法通过类的实例变量进行访问;可以通过公有函数接口与友元函数、友元类进行访问
**private**私有权限:一个类的private成员变量、成员函数,无法通过类的实例变量进行直接访问;可以通过公有函数接口与友元函数、友元类进行访问
对象
定义:
实例化一个事物,称为某个具体事物的一个个体,创建对象后才会分配具体内存空间
创建方式:
静态创建
动态创建
class Human
{
public:
private:
string name;
int age;
};
Human person; //静态创建
Human *person1 = new Human; //动态创建
访问属性及方法
针对静态创建的对象
<对象名>.<属性或方法>
Human person; //静态创建
person.name;
person.age;
针对动态创建的对象
<对象指针名>-><属性或方法>
Human *person1 = new Human;
person1->name;
person1->age;
构造函数
功能:
在构造对象时自动调用此函数,用户自定义的构造函数往往使用初始化对象内的成员变量
使用:
函数名与类名一致没有返回值
默认构造函数:
只有四种情况下,类中才会生产默认构造函数:
1.成员变量中存在带有默认构造函数的类对象
2.继承自带有默认构造函数的基类
3.该类中具有虚函数
4.继承自一个或多个虚基类
当使用无参构造函数静态创建一个对象的时候,不能添加小括号(),编译器默认此为函数声明
class Circle
{
public:
Circle();
private:
int x;
int y;
int r;
};
Circle::Circle()
{
cout << "default constructor" << endl;
}
无参构造函数
调用的时候,不要加()
//定义
Person::Person()
{
isFemale = true;
height = 165;
name = "XH";
age = 18;
}
//调用
//普通变量
Person p1;
//指针变量
Person *pa = new Person();
pa->show();
构造函数的重载:
调用的时候保证有相对的重载函数
#include <iostream>
using namespace std;
class Circle
{
public:
Circle();
Circle(int _x, int _y, int _r);
private:
int x;
int y;
int r;
};
Circle::Circle()
{
cout << "default constructor" << endl;
}
Circle::Circle(int _x, int _y, int _r)
{
x = _x;
y = _y;
r = _r;
cout << "Full constructor" << endl;
}
初始化列表:
在初始化时,也可以使用初始化列表的方式来实现构造函数,一般用在继承中
一般是全部成员都要初始化,除非初始化有问题的
先执行初始化列表,再之下函数体
class Circle
{
public:
Circle();
Circle(int _x, int _y, int _r);
private:
int x;
int y;
int r;
};
Circle::Circle()
{
cout << "default constructor" << endl;
}
//初始化列表
Circle::Circle(int _x, int _y, int _r) : x(_x), y(_y), r(_r)
{
cout << "Full constructor" << endl;
}
委托构造函数:
功能:
委托构造函数允许在同一个类中一个构造函数调用另外一个构造函数,可以在变量初始化时简化操作
使用:
一般是参数少的委托参数多的
委托构造函数:让别人干活
目标构造函数:干活的
注意点:
不要出现委托环
class Circle
{
public:
Circle();
Circle(int _x);
Circle(int _x, int _y, int _r);
private:
int x;
int y;
int r;
};
Circle::Circle()
{
cout << "default constructor" << endl;
}
//委托构造函数:让别人干活
Circle::Circle(int _x) : Circle(_x, 0, 0)
{
;
}
//目标构造函数:干活的
Circle::Circle(int _x, int _y, int _r) : x(_x), y(_y), r(_r)
{
cout << "Full constructor" << endl;
}
拷贝构造函数:
功能:
当一个对象需要通过另一个对象初始化,则需要使用拷贝构造函数
语法:
类名(const 类名 &other)
{
}
调用拷贝构造函数时机:
1、主动创建对象时,用一个对象去初始化另一个对象的时候
2、一个对象以**值传递**的形式传入函数体
3、一个对象以**值的形式**从函数返回
注意事项
1、译器默认生成的拷贝构造函数为浅拷贝(但是实际上,并不是一定会生成默认的拷贝构造函数)
1、成员变量中存在带有拷贝构造函数的类对象
2、继承自带有拷贝构造函数的基类
3、该类中存在虚函数
4、继承自一个或者多个虚基类
2、如果实现了拷贝构造函数,那么编译器不会生成默认构造函数,反之如果实现了默认构造函数,那么编译器还是会生成一个默认拷贝构造函数
3、const关键字可以省略,但是引用&不能省略,原因在于如果省略,那么形参接受值传递时发生拷贝构造,导致死循环
4、关于指针,因此除非与new一起使用,否则将不会调用任何构造函数
class Pointer
{
public:
Pointer();
Pointer(int _x, int _y = 100);
Pointer(const Pointer &other);
void show();
private:
int x;
int y;
};
// 复制构造函数,用于创建与现有Pointer对象相同的新的Pointer对象
// 参数 other: 一个现有的Pointer对象,从该对象复制x和y成员变量的值
Pointer::Pointer(const Pointer &other) : x(other.x) , y(other.y)
{
}
2、一个对象以 值 传递的形式传入函数体
void show(Pointer p)
{
}
3、一个对象以 值的形式 从函数返回
Pointer show_1()
{
Pointer p;
return p;
}
Pointer p;
1、主动创建对象时,用一个对象去初始化另一个对象的时候
//调用 拷贝构造函数
Pointer p1(p); //隐式调用 拷贝构造函数
Pointer p2 = p; //显示调用 拷贝构造函数
//不加编译选项是 全参构造(编译器优化)
//加上-fno-elide-constructors 会显示调用拷贝构造函数
/*
* 10 : Pointer temp(10) --> Full construct
* p3 = temp --> copy construct
* temp --> 释放
*/
Pointer p3 = 10;
深拷贝和浅拷贝
浅拷贝:
默认拷贝构造函数为浅拷贝
浅拷贝对指针类型变量,仅拷贝对应内存地址,不会开辟新内存空间
深拷贝:
认为重载构造函数,新开辟一段内存空间
//浅拷贝
/*
Circle::Circle(const Circle &other) : x(other.x), y(other.y), r(other.y)
{
cout << "Copy constructor" << endl;
}
*/
//深拷贝
Circle::Circle(const Circle &other) : x(other.x), y(other.y), r(other.y)
{
p = new int(*(other.p));
cout << "Copy constructor" << endl;
}
隐式拷贝构造函数:
1、一个对象作为函数参数,以值传递的方式传入函数体
2、一个对象作为函数返回值,以值传递的方式从函数返回
3、以A a = b的方式构造a,其中b也是A类型
移动构造函数:
功能:
所谓移动语义,指的就是以移动而非深拷贝的方式初始化含有指针成员的类对象。简单的理解,移动语义指的就是将其他对象(通常是临时对象)拥有的内存资源“移为已用”
语法:
类名(类名 &&other)
{
}
和拷贝构造函数的区别:
拷贝构造:

移动构造:

触发移动构造函数的时机:
如果临时对象即将消亡,并且它里面的资源是需要被再利用的,这个时候我们就可以触发移动构造。
Circle::Circle(Circle &&other) : x(other.x), y(other.y), r(other.y), p(other.p)
{
other.p = nullptr;
cout << "Move constructor" << endl;
}
std::move函数
可以以非常简单的方式将左值转换为右值引用。通过std::move,可以避免不必要的拷贝操作。std::move是为性能而生。
noexcept
用于指示函数是否可能抛出异常。在函数声明或定义时使用noexcept关键字可以提供编译器和程序员有关函数异常行为的信息,从而更好地管理异常处理和优化代码
使用场景
1、 在函数中使用
void myFunction() noexcept
{
// 函数实现
}
2、在表达式中使用
bool isSafe = noexcept(myFunction());
注意事项
1、如果函数声明为noexcept,而实际上在函数体中抛出了异常,C++标准要求调用 std::terminate 函数,导致程序终止。
2、noexcept可以用于优化,编译器可能会在知道函数不会抛出异常时进行一些优化
3、noexcept函数可以安全地调用可能会抛出异常的函数,而不必担心在异常发生时程序终止。
4、在异常安全的代码编写中,noexcept可以帮助确定何时调用可能会抛出异常的函数,以便正确处理异常
构造函数调用规则
拷贝构造函数的隐式调用
移动构造与拷贝构造的调用规则:
有移动调用移动
没有移动调用拷贝
没有拷贝直接报错
析构函数
功能:
对象释放的时候自动调用
释放对象内申请的资源,包括堆区的内存,线程,进程,文件描述符
定义:
与类名相同,在前面加位取反符~。且不能有任何参数与返回值
特点:
1、没有参数没有返回值,但有this指针
2、析构函数不能用const修饰
3、一个类有且只有一个析构函数,所以肯定不能重载
4、如果不手动销毁对象指针,程序运行结束时,编译器将不会进行处理,操作系统会直接进行内存回收,因此不会调用析构函数
this指针
功能:
用于保存对象的地址,每一个非静态函数中都有一个隐藏参数this
用法:
1、在非静态函数中,传入参数名与对象内成员变量相同时区分两个变量
2、在非静态函数中,需要返回对象
#include <iostream>
using namespace std;
class Person
{
public:
Person();
Person(int age, int height);
Person(const Person &other);
~Person();
void show();
Person setInfo(int age, int height);
private:
int age;
int height;
};
Person::Person() : age(0), height(0)
{
cout << "empty constructor" << endl;
}
Person::Person(int age, int height)
{
this->age = age;
this->height = height;
cout << "Full constructor" << endl;
}
Person::Person(const Person &other) : age(other.age), height(other.height)
{
cout << "Copy constructor" << endl;
}
Person::~Person()
{
cout << "destructor" << endl;
}
void Person::show()
{
cout << "age : " << age << endl <<
"height : " << height << endl;
}
Person Person::setInfo(int age, int height)
{
this->age = age;
this->height = height;
return *this;
}
int main(int argc, const char *argv[])
{
Person p1(18, 180);
p1.show();
p1.setInfo(20, 185).setInfo(22, 170);
p1.show();
return 0;
}
static
静态成员变量:
语法:
在类中**声明:**
static <类型修饰符><变量名>
在类外**初始化:**
<类型修饰符><类名>::<变量名> = <初始化值>
特点:
静态成员初始化,必须放在类外
static静态成员变量时所有**对象共享的**,并在对象创建之前就已经产生
static修饰的成员变量**属于类不属于具体的对象**
static成员变量的内存在程序开始运行时分配,程序运行结束时释放内存
**静态成员使用前必须初始化**,否则会在linker(链接)步骤出错
在成员函数中可以正常访问静态成员变量
对于公有静态成员变量,可以不需要构造对象直接访问,对于私有静态成员变量则不行
静态成员函数:
语法:
static <函数返回值><函数名>(参数列表)
特点:
1、静态成员函数**没有thie指针**,**与类相关**,不与对象关联,在调用静态函数时,可以在没有对象时调用
2、不能访问非静态成员,**只能访问静态成员**
3、可以被对象直接使用
4、可以通过类名直接访问
#include <iostream>
using namespace std;
class Pointer
{
public:
Pointer();
Pointer(int _x);
void show();
void setY(int _y);
static void setNoStaticX();
static void showInfo();
int x;
static int z;
private:
static int y;//类内定义
};
//静态成员变量,在类中声明
//类外初始化
int Pointer::y = 100;
int Pointer::z = 10;
Pointer::Pointer() : x(0)
{
cout << "empty constructor" << endl;
}
Pointer::Pointer(int _x) : x(_x)
{
cout << "full constructor" << endl;
}
void Pointer::show()
{
cout << "x : " << x << endl;
cout << "y : " << y << endl;
//静态成员函数,没有this指针,无法调用普通的非静态成员函数
//普通的非静态成员函数,拥有this指针,可以调用静态成员函数
this->setNoStaticX();
}
void Pointer::setY(int _y)
{
y = _y;
}
void Pointer::setNoStaticX()
{
//在静态成员函数中,不可以读写非静态成员变量
//可以读写静态成员变量
y = 300;
}
void Pointer::showInfo()
{
//静态成员函数中,可以调用其他静态成员函数
cout << "showInfo y : " << y << endl;
setNoStaticX();
}
int main(int argc, const char *argv[])
{
Pointer o1(1);
o1.show();
cout << "------------" << endl;
Pointer o2;
o2.setY(50);
cout << "o1 : ";
o1.show();
cout << "o2 : ";
o2.show();
cout << "------------" << endl;
o1.setNoStaticX();
o1.show();
Pointer::showInfo();
o1.show();
cout << "------------" << endl;
//通过类名可以直接访问静态成员变量
//同事也需要静态成员变量被public修饰
cout << "z : " << Pointer::z << endl;
cout << "------------" << endl;
//静态成员函数可以通过类名直接使用
//必须要在public权限下
Pointer::setNoStaticX();
return 0;
}
const
const成员变量
const成员变量**只能在初始化列表中初始化**
const成员函数 —核心:const修饰的是this指针
1、防止成员函数修改非静态成员变量的值,但可以修改静态成员变量(仍然可以访问所有成员变量的值)
2、const成员函数只能调用const成员函数,非const成员函数既能调用非const成员函数,也能调用const成员函数
3、const成员函数也可以与同名非const成员函数构成函数重载(本质修饰的时this指针,因此重载方式为类型不同)
#include <iostream>
using namespace std;
class Pointer
{
public:
Pointer(int _x, int _y);
//const修饰的是this指针
void show() const;
//被cosnt修饰的成员函数,可以发生重载
//重载的类型为参数类型不同
void show();
private:
int x;
const int y;
static int z;
};
int Pointer::z = 0;
Pointer::Pointer(int _x, int _y) : x(_x), y(_y)
{
cout << "full consturctor" << endl;
}
void Pointer::show() const
{
cout << "const show() : " << endl;
//对于非静态成员变量,不可以修改值,但是可以读取值
//对于非静态成员而言,读取和修改值都可以
z = 100;
cout << "x : " << x << " y : " << y << " z : " << z << endl;
}
//与const成员函数发生重载
//对于外部对象而言
//非const对象,调用非const成员函数
//const对象,调用const成员函数
void Pointer::show()
{
cout << "show() : " << endl;
cout << "x : " << x << " y : " << y << " z : " << z << endl;
}
int main(int argc, const char *argv[])
{
//对于非const对象而言,可以调用非const成员函数
//也可以调用const成员函数
Pointer p(10, 20);
p.show();
//针对于const对象而言,不能调用非const成员函数
const Pointer p2(20, 30);
p2.show();
return 0;
}
static和const之间调用关系
static成员变量属于整个类(因此在创建对象前就产生了,也必须初始化,不然链接阶段会报错),程序结束时释放内存,成员数据变量可以访问静态成员变量。公有静态数据成员可以直接访问,但私有静态成员变量不行
静态成员函数只能访问静态成员数据,无this指针,属于类而不属于某个对象,可以通过类名直接访问,在无对象时可以调用
const成员数据只能通过初始化列表进行初始化
static const修饰的成员函数只能修改s的值,const成员函数只能调用const成员函数。当const成员函数与同名非const成员函数会发生重载,const对象会调用const成员函数,非const对象调用非const成员函数(如果没有发送重载的话,普通对象就可以直接调用const或者普通成员函数)(本质上修饰的是this指针,属于参数类型不同)this实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给this。不过this这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默将它添加到参数列表中。这个额外的参数,实际上就是this,它是成员函数和成员变量之间的桥梁。故const成员变量不能访问非const成员函数
const修饰的成员函数:实际上修饰的是this指针指向空间的内容不可被修改,有了const修饰的成员函数,其this指针类型:const Date const* (左定值,右定向)而普通的成员函数:可以修改成员变量 ,其this指针类型:Date const*
| 成员类型 | 访问非静态成员变量 | 访问静态成员变量 | 调用非const函数 | 调用const函数 |
|---|---|---|---|---|
| 非static 非const | ✔️ | ✔️ | ✔️ | ✔️ |
| 非static const | ❌(除非 mutable) | ✔️ | ❌ | ✔️ |
| static 非const | ❌ | ✔️ | ❌(无 this) | ✔️(可调用静态函数) |
| static const | ❌ | ✔️ | ❌ | ✔️ |
友元函数
概念:
友元函数不是成员函数,它**定义在类的外部**,只需要在**类中声明**即可
友元函数可以访问所有私有成员和保护成员,一般情况下不适用,使用不当会破坏程序面向对象的特性
语法:
//friend <函数声明>
#include <iostream>
using namespace std;
class Box
{
public:
Box();
Box(int _w);
//声明友元函数
friend void printfBoxWidth(const Box box);
private:
int width;
};
Box::Box()
{
width = 0;
cout << "empty constructor" << endl;
}
Box::Box(int _w) : width(_w)
{
cout << "full constructor" << endl;
}
//友元函数的定义
//外部函数,能访问对象私有成员
void printfBoxWidth(Box box)
{
cout << "box width is " << box.width << endl;
}
int main(int argc, const char *argv[])
{
Box b;
printfBoxWidth(b);
Box b1(10);
printfBoxWidth(b1);
return 0;
}
友元类
概念:
**一个类中声明另一个类是自己的友元**
假如类A声明类B是自己的友元,那么在类B中可以访问类A所有的私有成员和保护成员
语法:
friend <类名>
注意事项:
1、友元不具有相互性,若类B是类A的友元,类A不一定是类B的友元
2、友元**不能被继承**(你爹的朋友不一定是你的朋友)
3、友元**不具有传递性**(你朋友的朋友不一定是你的朋友)
#include <iostream>
using namespace std;
class FriendTest;
class Box
{
//友元类
friend class FriendTest;
public:
Box();
Box(int _w);
void show();
private:
int width;
};
class FriendTest
{
public:
void setBox(Box &b, int _w);
private:
};
Box::Box()
{
width = 0;
cout << "empty constructor" << endl;
}
Box::Box(int _w) : width(_w)
{
cout << "full constructor" << endl;
}
void Box::show()
{
cout << "width : " << width << endl;
}
void FriendTest::setBox(Box &b, int _w)
{
b.width = _w;
}
int main(int argc, const char *argv[])
{
Box b;
Box b1(10);
b1.show();
FriendTest fb1;
fb1.setBox(b1, 20);
b1.show();
return 0;
}
内联函数
目的:
引入内联函数的目的是为了解决程序中函数**调用的效率**问题
原理:
内联函数在编译器阶段是直接复制 **“镶嵌”到主函数**中去的,就是将内联函数的代码直接放在内联函数的位置上,这与一般函数不同,主函数在调用一般函数的时候,是指令跳转到被调用函数的入口地址,执行完被调用函数后,指令再跳转回主函数上继续执行后面的代码;而由于内联函数是将函数的代码直接放在了函数的位置上,所以没有指令跳转,指令按顺序执行
语法:
**函数声明和定义必须在同一文件**进行,**声明与定义时需要添加inline**修饰符
内联函数内**不允许**使用<u>循环语句与开关语句</u>
内联函数不能超过10行,如果超过则编译器不会替换,而还是使用调用的方式调用函数
inline <返回值> <函数名>(<形参表>) {<函数体>}
类与内联函数:
1、类内定义的函数都是内联函数,不管是否有inline修饰符
2、声明在类外,定义在类外,看是否有inline修饰符
内联函数与宏定义的区别:
1、宏是由预处理器处理进行代码替换,而内联函数有编译器进行代码替换
2、内联函数时真正的函数
运算符重载
简介:
C++语言预定的运算符只能操作基础数据类型,对于用户自定义的类型,在进行运算时也需要类似的操作
运算符重载实际上是对运算符赋予新的运算方式
目的:
使对象的运算操作更加简洁明了
规则:
大部分运算符可以重载,少部分不行


重载运算符可以对运算符号做出新的解释,但基本语义不变
1、无法改变运算符优先级
2、无法改变运算符结构性
3、无法改变运算符所需要的操作数
4、无法创建新的运算符
语法:
类的成员函数
<函数返回值> operator <运算符> (<形参表>)
{
<函数体>
}
类的友元函数
friend <函数返回值> operator <运算符> (<形参表>)
{
<函数体>
}
#ifndef _overload_HPP
#define _overload_HPP
#include <iostream>
class OverLoad
{
public:
OverLoad();//无参构造
OverLoad(int _x, int _y);//全参构造
OverLoad(const OverLoad &other);//拷贝构造
OverLoad(OverLoad &&other);//移动构造
~OverLoad();
const OverLoad operator+(const OverLoad &other) const;
OverLoad &operator=(const OverLoad &other);
bool operator==(const OverLoad &other) const;
void show();
private:
int x;
int y;
};
#endif
#include "overload.hpp"
using namespace std;
OverLoad::OverLoad() : x(0), y(0)
{
cout << "empty constructor" << endl;
}
OverLoad::OverLoad(int _x, int _y) :
x(_x), y(_y)
{
cout << "full constructor" << endl;
}
OverLoad::OverLoad(const OverLoad &other) :
x(other.x), y(other.y)
{
cout << "copy constructor" << endl;
}
OverLoad::OverLoad(OverLoad &&other) :
x(other.x), y(other.y)
{
cout << "move constructor" << endl;
}
OverLoad::~OverLoad()
{
}
const OverLoad OverLoad::operator+(const OverLoad &other) const
{
OverLoad temp;
temp.x = this->x + other.x;
temp.y = this->y + other.y;
return temp;
}
OverLoad &OverLoad::operator=(const OverLoad &other)
{
this->x = other.x;
this->y = other.y;
return *this;
}
bool OverLoad::operator==(const OverLoad &other) const
{
if((this->x == other.x) && (this->y == other.y))
return true;
return false;
}
void OverLoad::show()
{
cout << "x : " << x << endl;
cout << "y : " << y << endl;
}
#include <iostream>
#include "overload.hpp"
using namespace std;
int main(int argc, const char *argv[])
{
OverLoad o1(10, 20);
cout << "o1 : ";
o1.show();
OverLoad o2 = (o1 + o1);
cout << "o2 : ";
o2.show();
OverLoad o3 = o2;
cout << "o3 : ";
o3.show();
cout << "o2 : ";
o3.show();
if(o3 == o1)
{
cout << "true" << endl;
}
else
{
cout << "false" << endl;
}
return 0;
}
1万+

被折叠的 条评论
为什么被折叠?



