1.内联函数
内联函数(
Inline Function
)是
C++
中一种特殊的函数,其定义直接在每个调用点展开。这意味着编译器
会尝试将函数调用替换为函数本身的代码,这样可以减少函数调用的开销,尤其是在小型函数中。
特点1. 减少函数调用开销 :内联函数通常用于优化小型、频繁调用的函数,因为它避免了函数调用的常规开销(如参数传递、栈操作等)。2. 编译器决策 :即使函数被声明为内联,编译器也可能决定不进行内联,特别是对于复杂或递归函数。3. 适用于小型函数 :通常只有简单的、执行时间短的函数适合做内联。4. 定义在每个使用点 :内联函数的定义(而非仅仅是声明)必须对每个使用它的文件都可见,通常意味着将内联函数定义在头文件中。
#include <iostream>
inline int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3); // 编译器可能会将此替换为:int result = 5 + 3;
std::cout << "Result: " << result << std::endl;
return 0;
}
在这个示例中,函数
add
被定义为内联函数。当它被调用时,编译器可能会将函数调用替换为函数体内的代码。
2.Lambda 表达式
Lambda
表达式是
C++11
引入的一种
匿名函数
的方式,它允许你在需要函数的地方内联地定义函数,而 无需单独命名函数。
Lambda
表达式的基本语法如下:
[ capture clause ]( parameters ) -> return_type {// 函数体// 可以使用捕获列表中的变量return expression ; // 可选的返回语句}
#include <iostream>
int main() {
int x = 10;
int y = 20;
// 捕获 x 和 y 以便在 Lambda 内部使用
// 这里的捕获列表 [x, y] 表示 x 和 y 被按值捕获
auto sum = [x, y]() {
// x++;
// y++; 按值捕获,关注的是值本身,无法修改
return x + y;
};
std::cout << "Sum is: " << sum() << std::endl;
std::cout << "x is now: " << x << ", y is now: " << y << std::endl;
// 捕获所有外部变量按值捕获(拷贝)
int z = 30;
auto multiply = [=]() {
// x++;
// y++; 按值捕获,关注的是值本身,无法修改
return x * y * z;
};
count << x << "," << y << endl;
std::cout << "Product is: " << multiply() << std::endl;
std::cout << "x is now: " << x << ", y is now: " << y << std::endl;
// 捕获所有外部变量按引用捕获
auto modifyAndSum = [&]() {
x = 15; // 修改 x 的实际值
y = 25; // 修改 y 的实际值, 引用捕获可以修改
return x + y;
};
std::cout << "Modified Sum is: " << modifyAndSum() << std::endl;
std::cout << "x is now: " << x << ", y is now: " << y << std::endl;
return 0;
}
按值捕获,不能修改值。
[=] 捕获外部所有的变量,[=]捕获外部所有的变量,但是可以修改值。
3类
#include <iostream>
using namespace std;
class hero{
public:
string name;
string type;
string place;
string guojia;
/*void printf_Nature()
{
string str="英雄名字是:"+name+",类型是:"+type+",主打位置是:"+place+",国家是:"+guojia;
cout<<str<<endl;
}
*/
void printf_Nature();
};
void hero::printf_Nature()
{
string str="英雄名字是:"+name+",类型是:"+type+",主打位置是:"+place+",国家是:"+guojia;
cout<<str<<endl;
}
int main()
{
hero hero1;
hero1.name="诺克萨斯之手";
hero1.place="上单";
hero1.type="战士";
hero1.guojia="诺克萨斯";
hero1.printf_Nature();
hero *hero2;
hero2=new(hero);
hero2->name="疾风剑豪";
hero2->place="中单";
hero2->type="刺客";
hero2->guojia="艾欧尼亚";
hero2->printf_Nature();
return 0;
}
我定义了一个hero类,然后通过用普通变量访问hero1,用指针变量访问hero2。
3.1 组合(qt中常用的用法)
在
C++
中,一个类包含另一个类的对象称为组合(
Composition
)。这是一种常见的设计模式,用
于表示一个类是由另一个类的对象组成的。这种关系通常表示一种
"
拥有
"
(
"has-a"
)的关系。
#include <iostream>
using namespace std;
class equipt
{
public:
string jincheng;
string shoes;
void print_equipt();
};
void equipt ::print_equipt()
{
cout<<"购买了一件进程武器:"<<jincheng<<endl;
cout<<"购买了一件鞋子:"<<shoes<<endl;
}
class hero{
public:
string name;
string type;
string place;
string guojia;
equipt *pe2;
void printf_Nature();
};
void hero::printf_Nature()
{
string str="英雄名字是:"+name+",类型是:"+type+",主打位置是:"+place+",国家是:"+guojia;
cout<<str<<endl;
}
int main()
{
hero hero1;
hero1.name="诺克萨斯之手";
hero1.place="上单";
hero1.type="战士";
hero1.guojia="诺克萨斯";
hero1.printf_Nature();
hero1.pe2=new(equipt);
hero1.pe2->jincheng="三相之力";
hero1.pe2->shoes="布甲鞋";
hero1.pe2->print_equipt();
hero *hero2;
hero2=new(hero);
cout<<"er"<<endl;
hero2->name="疾风剑豪";
hero2->place="中单";
hero2->type="刺客";
hero2->guojia="艾欧尼亚";
hero2->pe2=new(equipt);
hero2->pe2->jincheng="女妖杀手";
hero2->pe2->shoes="攻速鞋";
hero2->printf_Nature();
hero2->pe2->print_equipt();
return 0;
}
我在之前的基础上,定义了一个equipt类,将他放在第一个类中,然后通过普通变量访问hero类中的成员变量以及equipt。
3.2 权限(银行案例)
- public:表示该成员可以被类的任何对象或者其他任何函数访问。
- protected:表示该成员可以被其所属类的任何成员函数以及派生类的成员函数访问,但不能从类外部直接访问。
- private:表示该成员只能被其所属类的成员函数访问,不能从类外部或派生类直接访问。
注意:类中不写权限,默认是私有权限
下列代码为银行存取款的一个示例:
#include <iostream>
#include <string.h>
using namespace std;
class bankAccount
{
private:
string name;
string addr;
int age;
double cunkuan;
public:
void registerMes(string newName,string newAddr,int newAge,double newCunkuan);
void printusrInfo();
void addMoney(double amount);
void getMoney(double amount);
double getYue();
};
void bankAccount::registerMes(string newName,string newAddr,int newAge,double newCunkuan)
{
name=newName;
addr=newAddr;
age=newAge;
cunkuan=newCunkuan;
}
void bankAccount::printusrInfo()
{
string str="账户名:"+name+",地址:"+addr+",年龄:"+std::to_string(age)+",当前存款:"+std::to_string(cunkuan);
cout<<str<<endl;
}
void bankAccount::addMoney(double amount)
{
if(amount<0)
{
cerr<<"Addmoney error"<<endl;
}
else
{
cunkuan+=amount;
}
}
void bankAccount::getMoney(double amount)
{
if(amount>cunkuan)
{
cerr<<"Getmoney error1"<<endl;
}else if(amount<0)
{
cerr<<"Getmoney error2"<<endl;
}else
{
cunkuan-=amount;
}
}
double bankAccount::getYue()
{
cout<<"当前余额为:"<<endl;
return cunkuan;
}
int main()
{
bankAccount usr1;
usr1.registerMes("小王","陕西省渭南市",22,100);
usr1.printusrInfo();
usr1.addMoney(1000);
cout<<usr1.getYue()<<endl;
usr1.getMoney(300);
cout<<usr1.getYue()<<endl;
return 0;
}
我将客户的信息,包括名字,住址,年龄,存款等设为私有权限,客户不能直接修改信息的内容,智能通过公有成员里面的注册函数来对信息进行修改,通过公有的成员来进行存钱,取钱,得到当前余额的功能。
3.3 引用
3.3.1 引用
#include <iostream>
using namespace std;
int main ()
{
// 声明简单的变量
int i;
double d;
// 声明引用变量
int& r = i;
double& s = d;
i = 5;
cout << "Value of i : " << i << endl;
cout << "Value of i reference : " << r << endl;
d = 11.7;
cout << "Value of d : " << d << endl;
cout << "Value of d reference : " << s << endl;
return 0;
}
Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7
3.3.2 引用和指针的区别
引用很容易与指针混淆,它们之间有三个主要的不同:
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
- 引用必须在创建时被初始化。指针可以在任何时间被初始化。
#include <iostream>
using namespace std;
// 函数声明
void swap(int& x, int& y);
void pswap(int* x, int* y);
void yswap(int& x, int& y);
int main ()
{
// 局部变量声明
int a = 100;
int b = 200;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
/* 调用函数来交换值 */
swap(a, b);
cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;
// 局部变量声明
int a = 300;
int b = 400;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
/* 调用函数来交换值 */
pswap(a, b);
cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;
// 局部变量声明
int a = 500;
int b = 600;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
/* 调用函数来交换值 */
yswap(a, b);
cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;
return 0;
}
// 普通实现
void swap(int x, int y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
return;
}
// 指针实现
void pswap(int* x, int* y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 赋值给 x */
*y = temp; /* 把 x 赋值给 y */
return;
}
// 引用实现
void yswap(int& x, int& y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
return;
}
运行结果:
交换前,
a
的值:
100
交换前,
b
的值:
200
交换后,
a
的值:
100
交换后,
b
的值: 2
00
交换前,
a
的值:
300
交换前,
b
的值: 4
00
交换后,
a
的值: 4
00
交换后,
b
的值: 3
00
交换前,
a
的值: 5
00
交换前,
b
的值: 6
00
交换后,
a
的值: 6
00
交换后,
b
的值: 5
00
3.3.3 把引用作为返回值
通过使用引用来替代指针,会使
C++
程序更容易阅读和维护。
C++
函数可以返回一个引用,方式与返回 一个指针类似
#include <iostream>
using namespace std;
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues(int i) {
double& ref = vals[i];
return ref; // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i]
}
// 要调用上面定义函数的主函数
int main ()
{
cout << "改变前的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
setValues(1) = 20.23; // 改变第 2 个元素
setValues(3) = 70.8; // 改变第 4 个元素
cout << "改变后的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
return 0;
}
改变前的值
vals
[
0
]
=
10.1
vals
[
1
]
=
12.6
vals
[
2
]
=
33.1
vals
[
3
]
=
24.1
vals
[
4
]
=
50
改变后的值
vals
[
0
]
=
10.1
vals
[
1
]
=
20.23
vals
[
2
]
=
33.1
vals
[
3
]
=
70.8
vals
[
4
]
=
50
注意:
当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。
int& func() {
int q;
//! return q; // 在编译时发生错误
static int x;
return x; // 安全,x 在函数作用域外依然是有效的
}
3.4 函数重载
在同一个作用域内,可以声明几个功能类似的同名函数, 这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}
void print(double f) {
cout << "浮点数为: " << f << endl;
}
void print(char c[]) {
cout << "字符串为: " << c << endl;
}
};
int main(void)
{
printData pd;
// 输出整数
pd.print(5);
// 输出浮点数
pd.print(500.263);
// 输出字符串
char c[] = "Hello C++";
pd.print(c);
return 0;
}
3.5 构造函数
类的
构造函数
是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回
void
。构造函数可用于 为某些成员变量设置初始值。
3.5.1 无参构造
#include <iostream>
#include <string>
using namespace std; // 使用std命名空间
class Car {
public:
string brand; // 不需要使用std::string
int year;
// 无参构造函数
Car() {
brand = "未知";
year = 0;
cout << "无参构造函数被调用" << endl; // 不需要使用std::cout和std::endl
}
void display() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};
int main() {
Car myCar; // 创建Car对象
myCar.display(); // 显示车辆信息
return 0;
}
默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。
3.5.2 带参构造
#include <iostream>
#include <string>
using namespace std;
class Car {
public:
string brand;
int year;
// 带参数的构造函数,使用常规的赋值方式
Car(string b, int y) {
brand = b;
year = y;
}
void display() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};
int main() {
Car myCar("Toyota", 2020); // 使用带参数的构造函数创建Car对象
myCar.display(); // 显示车辆信息
return 0;
}
3.5.3 使用初始化列表
在
C++
中,使用初始化列表来初始化类的字段是一种高效的初始化方式,尤其在构造函数中。初始化列表直接在对象的构造过程中初始化成员变量,而不是先创建成员变量后再赋值。这对于提高性能尤其重要,特别是在涉及到复杂对象或引用和常量成员的情况下。
初始化列表紧跟在构造函数参数列表后面,以冒号(
:
)开始,后跟一个或多个初始化表达式,每个表达式通常用逗号分隔。下面是使用初始化列表初始化字段的例子:
class MyClass {
private:
int a;
double b;
std::string c;
public:
// 使用初始化列表来初始化字段
MyClass(int x, double y, const std::string& z) : a(x), b(y), c(z) {
// 构造函数体
}
};
在这个例子中,
MyClass
有三个成员变量:
a
(
int
类型)、
b
(
double
类型)和
c
( std::string
类型)。当创建
MyClass
的一个实例时,我们通过构造函数传递三个参数,这些参数 被用于通过初始化列表直接初始化成员变量。初始化列表 : a(x), b(y), c(z)
的意思是用
x
初始化
a
,用
y
初始化
b
,用
z
初始化
c
。
初始化列表的优点包括:1. 效率 :对于非基本类型的对象,使用初始化列表比在构造函数体内赋值更高效,因为它避免了先默认构造然后再赋值的额外开销。2. 必要性 :对于引用类型和常量类型的成员变量,必须使用初始化列表,因为这些类型的成员变量在构造函数体内不能被赋值。3. 顺序 :成员变量的初始化顺序是按照它们在类中声明的顺序,而不是初始化列表中的顺序。使用初始化列表是 C++ 中推荐的初始化类成员变量的方式,因为它提供了更好的性能和灵活性。
3.5.4 this 关键字
this 关键字是一个指向调用对象的指针。
它在成员函数内部使用,用于引用调用该函数的 对象。使用 this
可以明确指出成员函数正在操作的是哪个对象的数据成员。
#include <iostream>
#include <string>
using namespace std;
class Car {
private:
string brand;
int year;
public:
Car(string brand, int year) {
this->brand = brand;
this->year = year;
// cout << "构造函数中:" << endl;
// cout << this << endl;
}
void display() const {
cout << "Brand: " << this->brand << ", Year: " << this->year << endl;
// 也可以不使用 this->,直接写 brand 和 year
}
Car& setYear(int year) {
this->year = year; // 更新年份
return *this; // 返回调用对象的引用
}
};
int main()
{
Car car("宝马",2024);
car.display();
// 链式调用
car.setYear(2023).display();
// cout << "main函数中:" << endl;
// cout << &car << endl;
// Car car2("宝马",2024);
// cout << "main函数中:" << endl;
// cout << &car2 << endl;
return 0;
}
从代码中可以看出,当构造函数的参数与类中的变量名字一样时,为了区分,可以通过this关键字来解决。
应用返回调用对象的引用可以返回this 指针。主要应用在链式应用上。
3.5.5 new和delete
基本用法
分配单个对象
:使用
new
可以在堆上动态分配一个对象。例如,
new int
会分配一个
int
类型的空
间,并返回一个指向该空间的指针。
int* ptr = new int ; //C 语言中, int *p = (int *)malloc(sizeof(int));
分配对象数组
:
new
也可以用来分配一个对象数组。例如,
new int[10]
会分配一个包含
10
个整数的数组。
初始化
:可以在
new
表达式中使用初始化。对于单个对象,可以使用构造函数的参数:
MyClass * obj = new MyClass ( arg1 , arg2 );
使用
new
分配的内存必须显式地通过
delete
(对于单个对象)或
delete[]
(对于数组)来释放,以 避免内存泄露。
释放单个对象:
delete ptr ; // 释放 ptr 指向的对象
释放数组:
delete [] arr ; // 释放 arr 指向的数组
class MyClass {
public:
MyClass() {
std::cout << "Object created" << std::endl;
}
};
int main() {
// 分配单个对象
MyClass* myObject = new MyClass();
// 分配对象数组
int* myArray = new int[5]{1, 2, 3, 4, 5};
// 使用对象和数组...
// 释放内存
delete myObject;
delete[] myArray;
return 0;
}
3.6 析构函数
析构函数是
C++
中的一个特殊的成员函数,它在对象生命周期结束时被自动调用,用于执行对象销毁前的清理工作。析构函数特别重要,尤其是在涉及动态分配的资源(如内存、文件句柄、网络连接等)的情况下。
基本特性1. 名称 :析构函数的名称由波浪号( ~ )后跟类名构成,如 ~MyClass() 。2. 无返回值和参数 :析构函数不接受任何参数,也不返回任何值。3. 自动调用 :当对象的生命周期结束时(例如,一个局部对象的作用域结束,或者使用 delete 删除一个动态分配的对象),析构函数会被自动调用。4. 不可重载 :每个类只能有一个析构函数。5. 继承和多态 :如果一个类是多态基类,其析构函数应该是虚的。
师:上官可编程