目录
【1】函数
1》内联函数 inline
在C++中使用内联函数主要是为了取代语言中的宏定义的函数,内联函数在编译时,可以直接展开函数体到主函数中,以此提高程序执行的效率,消除了普通的额外开销。
建议将代码长度较小(1-5行且不能包含复杂控制语句)且频繁使用的函数定义为内联函数,内联函数只是给编译器的建议,编译器并一定采纳。
只需要在函数定义处使用inline 关键字修饰函数,就可以把函数设置成内联函数。
#include <iostream>
using namespace std;
/**
* @brief test 内联函数
*/
inline void test(int a)
{
cout << "fdfd" << endl;
cout << ++a << endl;
}
void func(); // 函数声明
inline void func() // 函数定义
{
cout << "翻江倒海发的" << endl;
cout << "法国活动经费" << endl;
}
int main()
{
int a = 1;
test(a);
func();
return 0;
}
2》函数重载 overload
C++ 中允许同一个函数名称定义多个函数,这就是函数重载。
函数签名是编译器区分不同函数的方式,包括一下几个组成部分:
1.函数名称
2.参数数量
3.参数类型
两个函数的签名不可以相同,即一个函数只有一个独一无二的签名。
当函数中函数名称相同,剩余条件不同时(参数数量或参数类型不同),就构成了函数重载。
#include <iostream>
using namespace std;
void test()
{
cout << "没有参数" << endl;
}
void test(int a)
{
cout << "一个int参数" << a << endl;
}
void test(int a,int b)
{
cout << "两个int参数" << a+b << endl;
}
void test(string a,string b)
{
cout << "两个string参数" << a+b << endl;
}
//int test(string a,string b) 错误
//{
// cout << "两个string参数" << a+b << endl;
// return 1;
//}
int main()
{
test(1,3);
test(54);
test();
test("aa","bb");
return 0;
}
需要注意的是:构造函数可以重载,析构函数不可以重载
3》函数的参数默认(缺省)值
C++允许给函数设定默认值,调用函数时如果传入参数,传入的参数会覆盖默认值;调用时如果不传递参数,参数使用预设的默认值。
函数的参数默认值既可以在声明处(建议)设定,又可以在定义处设定,但是只能出现一次。
向右(后)原则:如果函数的参数有多个,只要某个参数设定了默认值,其右边(后面)的所有参数都要设定默认值。
#include <iostream>
using namespace std;
void func(int a = 1)
{
cout << a << endl;
}
void test(int a = 2);
void test(int a)
{
cout << a << endl;
}
void method(int a,int b = 1,int c = 2)
{
cout << a << b << c << endl;
}
int main()
{
func(); // 1
func(2); // 2
test(); // 2
test(1); // 1
method(6); // 612
method(6,6); // 662
method(6,6,6); // 666
return 0;
}
尽量不要同时使用函数重载和参数默认值,因为非常容易出现二义性问题。
#include <iostream>
using namespace std;
void method(int a,int b = 1,int c = 2)
{
cout << "A" << a << b << c << endl;
}
void method(int a)
{
cout << "B" << a << endl;
}
int main()
{
method(8,8,8); // A888
method(5,5); // A552
// method(9); 错误
return 0;
}
4》哑元函数
一个函数的参数只有类型没有名字,这个参数就是哑元。包含哑元的函数就是哑元函数。
#include <iostream>
using namespace std;
void test(int)
{
cout << "AAA" << endl;
}
int main()
{
// test(); 错误
test(34897);
return 0;
}
哑元函数的实际功能包括但不限于:
1.在C++的运算符重载中区分重载函数
2.保持函数的向前兼容性
【2】类和对象
类:一个抽象的概念,用于描述同一类对象的特征。在现在学习阶段,一个单独的类没有任何功能。
对象:根据类的描述创造的实体。
1》类的定义
类中主要包含两个部分:
1.属性 property (成员变量 member value 或数据成员)
在类中存在的变量,用于存储数据,通常是一个名词,例如身高、价格、体重......
2.行为 (成员函数 member function)
可以执行的功能,是类中的函数,通常是一个动词或动词词组,例如吃饭、玩游戏、关闭、运行.....
成员 = 成员变量 + 成员函数
例:定义一个手机类
#include <iostream>
using namespace std;
/**
* @brief The MobilePhone class
* 手机类
*/
class MobilePhone // 帕斯卡(大驼峰)命名法:所有单词首字母大写
{
public: // 表示访问不受限
string brand; // 品牌
string model; // 型号
int weight; // 重量
void play_music()
{
cout << "How to Love" << endl;
}
void run_game()
{
cout << "金铲铲之战" << endl;
}
void communicate()
{
cout << "摩西摩西" << endl;
}
};
2》创建对象
C++支持两种对象:
栈内存对象
在生命周期(生命周期为所在的函数{})结束后,自动被回收,调用成员使用
堆内存对象
必须使用 new 关键字创建对象,使用指针保存对象首地址,使用delete 销毁对象,如果创建之后没有手动销毁,对象会持续存在,造成内存泄漏。
#include <iostream>
using namespace std;
class MobilePhone
{
public:
string brand;
string model;
int weight;
void play_music()
{
cout << "Remedy" << endl;
}
void run_game()
{
cout << "炉石传说" << endl;
}
void communicate()
{
cout << "喂?" << endl;
}
};
int main()
{
// 栈内存对象
MobilePhone mp1;
// 调用成员变量,先赋值,再读取输出
mp1.brand = "苹果";
mp1.model = "16 Pro Max";
mp1.weight = 200;
cout << mp1.brand << endl;
cout << mp1.model << endl;
cout << mp1.weight << endl;
// 调用成员函数
mp1.communicate();
mp1.play_music();
mp1.run_game();
// 堆内存对象
MobilePhone* mp2 = new MobilePhone;
mp2->brand = "华为";
mp2->model = "非凡大师xt1";
mp2->weight = 301;
cout << mp2->brand << endl;
cout << mp2->model << endl;
cout << mp2->weight << endl;
mp2->communicate();
mp2->play_music();
mp2->run_game();
delete mp2; // 销毁mp2
// 不要销毁后还使用
// cout << mp2->brand << endl;
// mp2->communicate();
return 0;
} // mp1销毁
【3】封装
类的定义与结构体很相似,因为结构体实际上就是一种完全开放的类。类通常需要进行封装,封装指的是先将类的一些属性和细节隐藏,再重新提供外部调用接口。
#include <iostream>
using namespace std;
class MobilePhone
{
private: // 私有:被修饰的成员只能在类内访问
string brand; // 读写
string model = "16"; // 只读
int weight; // 只写
public:
string get_brand() // getter:读函数
{
return brand;
}
void set_brand(string b) // setter:写函数
{
brand = b;
}
string get_model()
{
return model;
}
void set_weight(int w)
{
weight = w;
}
};
int main()
{
MobilePhone mp1;
// 调用setter设置属性值
mp1.set_brand("华为");
mp1.set_weight(300);
// 调用getter获取属性值
cout << mp1.get_brand() << endl;
cout << mp1.get_model() << endl;
// TODO 可以试试堆内存对象
return 0;
}
封装的意义在于让程序员站在外部视角看待整个对象整体,忽略内部细节,同时可以提高代码的安全性和可维护性。
【4】构造函数 constructor
1》基础使用
类中有一种特殊的成员函数,在创建对象的必须调用,这个函数就是构造函数,构造函数的特殊性体现在:
1.不写返回值
2.函数名称必须是类名
3.如果程序员不手动编写构造函数,编译器会自动添加一个无参且函数体为空的构造函数。
构造函数经常用于子啊创建对象时进行对象属性初始化,构造函数也支持参数默认值和重载。
#include <iostream>
using namespace std;
class MobilePhone
{
private:
string brand;
string model;
int weight;
public:
// 编译器会在程序员不写的情况下添加下面的构造函数
// MobilePhone(){}
MobilePhone()
{
// 给属性赋予初始值
brand = "山寨";
model = "???";
weight = 188;
cout << "构造函数" << endl;
}
MobilePhone(string b,string m,int w)
{
brand = b;
model = m;
weight = w;
cout << "构造函数2" << endl;
}
/**
* @brief show 输出所有属性值
*/
void show()
{
cout << brand << endl;
cout << model << endl;
cout << weight << endl;
}
};
int main()
{
MobilePhone mp1;
mp1.show();
MobilePhone* mp2 = new MobilePhone;
mp2->show();
delete mp2;
MobilePhone mp3("魅族","Lucky08",199);
mp3.show();
MobilePhone* mp4 = new MobilePhone("小米","su7",2100000);
mp4->show();
delete mp4;
cout << "主函数结束" << endl;
return 0;
}
2》构造初始化列表
3》构造函数的调用方式
构造函数可以显示调用,也可以隐式调用
显式调用:在创建对象时使用明确的构造函数调用语法,不能被 explict 关键字影响。
隐式调用:在创建对象时不使用明确的构造函数调用语法,受到 explict 关键字影响。
#include <iostream>
using namespace std;
class Student
{
private:
string name;
public:
Student(string n):name(n)
{
cout << "构造函数,name=" << name << endl;
}
string get_name()
{
return name;
}
};
int main()
{
Student s1("张三");
cout << s1.get_name() << endl;
Student* s2 = new Student("李四");
cout << s2->get_name() << endl;
delete s2;
string name = "王五";
Student s3 = name; // 编译器帮忙调用了构造函数
cout << s3.get_name() << endl;
Student s4(name); // 编译器帮忙调用了构造函数
cout << s4.get_name() << endl;
return 0;
}
使用 explict 关键字可以屏蔽隐式调用的构造函数
#include <iostream>
using namespace std;
class Student
{
private:
string name;
public:
// 明确的
explicit Student(string n):name(n)
{
cout << "构造函数,name=" << name << endl;
}
string get_name()
{
return name;
}
};
int main()
{
Student s1("张三"); // 显式
cout << s1.get_name() << endl;
Student* s2 = new Student("李四"); // 显式
cout << s2->get_name() << endl;
delete s2;
string name = "王五";
// Student s3 = name; // 隐式,被explicit屏蔽
// cout << s3.get_name() << endl;
Student s4(name); // 显式
cout << s4.get_name() << endl;
return 0;
}
4》拷贝构造函数
1> 概念
如果程序员不手动编写拷贝构造函数,编译器会为每个类增加一个拷贝构造函数。
通过拷贝构造函数创建的新对象,只是属性值与源对象相同,但是会在新的地址空间进行开辟。
#include <iostream>
using namespace std;
class Student
{
private:
string name;
public:
Student(string n):name(n)
{
cout << "构造函数,name=" << name << endl;
}
// 编译器自动添加的拷贝构造函数
// Student(const Student& s)
// {
// name = s.name;
// }
Student(const Student& s)
{
name = s.name;
cout << "拷贝构造函数" << endl;
}
string get_name()
{
cout << &name << endl;
return name;
}
};
int main()
{
Student s1("张三");
cout << s1.get_name() << endl;
Student s2(s1);
cout << s2.get_name() << endl;
return 0;
}
2> 浅拷贝
当类中的成员变量出现指针时,默认的拷贝构造函数会出现浅拷贝的现象。
#include <iostream>
#include <string.h> // 头文件
using namespace std;
class Student
{
private:
char* name;
public:
Student(char* n):name(n)
{
cout << "构造函数" << endl;
}
char* get_name()
{
return name;
}
};
int main()
{
char name[20] = "张三";
Student s1(name);
Student s2(s1);
strcpy(name,"李四");
cout << s1.get_name() << endl; // 李四
cout << s2.get_name() << endl; // 李四
return 0;
}
在上面的代码中,在31行修改了26行的字符数组内容,s1和s2隐藏的成员变量的值就变了,且s1和s2的name保存的地址就是26行的name,这都不符合面向对象的特性。
3> 深拷贝
深拷贝的思路是在浅拷贝每次对指针进行赋值时,都单独开辟一块内存给要赋值的变量。
#include <iostream>
#include <string.h> // 头文件
using namespace std;
class Student
{
private:
char* name;
public:
Student(char* n):name(new char[20])
{
// 同步两块内存的内容
strcpy(name,n);
cout << "构造函数" << endl;
}
Student(const Student& s)
{
// 同步两块内存的内容
name = new char[20];
strcpy(name,s.name);
cout << "拷贝构造函数" << endl;
}
char* get_name()
{
return name;
}
};
int main()
{
char name[20] = "张三";
Student s1(name);
Student s2(s1);
strcpy(name,"李四");
cout << s1.get_name() << endl; // 张三
cout << s2.get_name() << endl; // 张三
return 0;
}
在上面的深拷贝代码中,new出来的空间并没有delete回收,因此会造成内存泄漏问题。
【5】 析构函数 destructor
析构函数是与构造函数对立的函数,也是一种特殊的成员函数。
构造函数 | 析构函数 |
函数名称为类名 | 函数名称为 ~类名 |
功能:创建对象并初始化 | 功能:在对象销毁时回收资源 |
在创建对象时调用 | 在对象销毁时调用 |
有参数,支持重载和默认值 | 无参数,不支持重载和默认值 |
如果程序员没有手动写一个析构函数,编译器也自动添加一个的析构函数:~类名(){}
#include <iostream>
using namespace std;
class Dog
{
public:
~Dog()
{
cout << "析构函数" << endl;
}
};
int main()
{
Dog d1;
Dog* d2 = new Dog;
delete d2; // d2输出:析构函数
cout << "主函数结束" << endl;
return 0;
} // d1输出:析构函数
回到深拷贝代码中,可以在析构函数里释放堆内存资源,在Student中添加下面的代码:
标签:构造函数,cout,day02,namespace,include,string,函数 From: https://blog.csdn.net/dghbs/article/details/142615310今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧!