一、编译部分操作
生成解决方案需要与生成0718在当前只有一个工程的情况下是一样的。
生成解决方案通常比重新生成解决方案更快,因为它只编译那些自上次生成以来已经更改的文件。
二、调试过程
开始调试需要设置断点,否则一闪结束。
开始执行是用来观察结果。
1、设置断点
2、调试包括单步调试、继续(F5)、停止调试(shift+F5)等功能。
单步调试有两种:包括逐过程(F10)和逐语句(F11)。两者的区别在于:
逐语句是跟踪函数的,在函数调用时会进入函数,你可以逐步查看函数内部的执行流程和变量状态。
逐过程会跳出函数走向下一行,当你对某个函数的功能已经有所了解,或者确信函数内部没有问题,而只是想快速查看函数调用的结果,并继续执行后续代码。
继续功能主要是跳过该断点向下执行,直到下一个断点停止。
停止调试是在发现问题之后,选择直接退出。
三、数据类型
基本数据类型
- 整型(Integer Types):
int
:通常用于存储整数。short
:比int
更小的整数类型。long
:比int
更大的整数类型。long long
(C99和C++11引入):比long
更大的整数类型。unsigned
:表示无符号类型,可以与上述任何类型结合使用,如unsigned int
,表示非负整数。char
:通常用于存储字符,但在C/C++中也可以用来存储小的整数值(如ASCII码)。
- 浮点型(Floating-Point Types):
float
:单精度浮点数。double
:双精度浮点数。long double
:扩展精度浮点数(在某些平台上,可能与double
相同)。
- 布尔型(Boolean Types):
- C语言标准(C99之前)没有原生的布尔类型,但
<stdbool.h>
头文件(C99及以后)提供了bool
类型。 - C++从一开始就支持
bool
类型,并且是标准库的一部分。
- C语言标准(C99之前)没有原生的布尔类型,但
枚举类型(Enumerations)
- 枚举(Enum):在C和C++中,枚举类型允许开发者为整数常量指定名称,使代码更加清晰。
指针类型
- 指针(Pointer):在C和C++中,指针是非常重要的类型,它存储了变量的内存地址。指针可以用于数组、动态内存分配、函数参数等多种场景。
复合数据类型
- 结构体(Struct):允许将不同类型的数据项组合成一个单一的类型。
- 联合体(Union):允许在相同的内存位置存储不同的数据类型,但一次只能使用其中一种。
- 数组(Array):一组相同类型的数据项,可以通过索引访问。
C++特有数据类型
- 类(Class):C++中的核心特性,支持面向对象编程的封装、继承和多态。
- 模板(Template):支持泛型编程,允许定义与类型无关的函数和类。
- 字符串(String):C++标准库中的
std::string
类提供了比C风格的字符数组更安全和更灵活的字符串处理能力。 - 引用(Reference):C++中的引用是变量的别名,用于函数参数传递时避免拷贝,或作为函数的返回值。
- 智能指针(Smart Pointers):如
std::unique_ptr
、std::shared_ptr
等,用于自动管理动态分配的内存,减少内存泄漏的风险。
四、变量定义
1、比照数据类型分配对应大小空间
2、取变量名(字母、数字或下划线的组合,但不能以数字开头。区分大小写)
3、变量可以在定义时初始化,也可以在定义稍后初始化。
4、变量一定有首地址。
5、局部变量(在函数内部定义的变量)在函数执行完毕后会被销毁,而全局变量(在所有函数外部定义的变量)在整个程序执行期间都存在。
五、函数参数
函数参数中有三种,包括值传递、址传递、以及引用。在平常使用中,优先考虑引用,然后考虑指针(灵活),最后考虑值传递。
#include"stdafx.h"
#include <iostream>
using namespace std;
void test(int a, int *p, int &b) {
++a;//a1作为值传递给a,在函数内部对a的修改不会影响a1的值。
++(*p);//&a2作为地址传递给p,通过解引用*p,可以访问并修改a2的值。因此,a2增加 1。
++b;//a3通过引用传递给b,这意味着b就是a3的别名。因此,对b的任何修改都会直接反映在a3上。a3加1.
}
int main()
{
int a1 = 0;
int a2 = 0;
int a3 = 0;
test(a1, &a2, a3);
cout << a1 << endl;
cout << a2 << endl;
cout << a3 << endl;
}
六、输入输出
cin 和 cout,分别用于从键盘输入数据和向屏幕输出数据(简称为标准 I/O)。除此之外,程序还可以从文件中读入数据,以及向文件中写入数据(简称为文件 I/O)。
cin和cout的声明在<iostream>头文件中,属于std命名空间。
1、cout
功能:计算表达式的值后输出
输出时根据值类型自动显示正确的形式。
示例:乘法口诀表
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
cout << j << "*" << i << "=" << j * i << " ";
}
cout << endl;
}
return 0;
}
2、cin
功能:从键盘上输入数据保存到相应的变量中,数据间用至少一个空格或Tab符或换行隔开,数据的转化由系统完成。
示例:
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
char name[32] = { 0 };
int age = 0;
cout << "请输入姓名和年龄:" << endl;
cin >> name >> age;
cout << name << "\t"
<< age << endl;
return 0;
}
七、动态内存释放
堆是操作系统维护的一块内存,而自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。
1、动态分配单个元素内存并释放。
小括号表示赋初始值
动态分配内存, 小括号表示初始值
在堆上申请1个int类型4个字节的存储空间,并将申请的存储空间首地址存放到变量p中。
int * p = new int(10);//等价于 int * p = (int *)malloc (sizeof(int));
cout << *p << endl;
delete p;//释放在堆上申请的存储空间,释放完成后将指针变量p赋为NULL,避免野指针。
p = NULL;
2、冒泡排序+动态分配数组内存
中括号表示元素个数。
int n = 0;
cout << "请输入待输入数据的个数:";
cin >> n;
int * p = new int[n]; //在堆上申请n个int类型存储空间,并将申请的存储空间首地址存放到变量p中。
cout << "请输入" << n << "个整数:" << endl;
for (int i = 0; i < n; i++) {
cin >> p[i];
}
//for (int i = 0; i < n; i++) {
// for (int j = 0; j < n - i - 1; j++) {
// int temp= 0;
// if (p[j] < p[j + 1]) {//降序
// temp = p[j];
// p[j] = p[j + 1];
// p[j + 1] = temp;
// }
// }
//}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i - 1; j++) {
int temp = 0;
if (p[j] > p[j + 1]) {//升序
temp = p[j];
p[j] = p[j + 1];
p[j + 1] = temp;
}
}
}
for (int i = 0; i < n; i++) {
cout << p[i];
}
cout << endl;
delete[]p;//申请的是数组,释放的时候必须用delete[]。
return 0;
delete与free区别
delete是关键字,可调用析构函数,也可释放内存。
free是库函数,可以释放内存。
八、安全函数
安全函数是指那些能够减少或避免缓冲区溢出、内存泄漏等安全问题的函数。这些函数通常要求程序员明确指定输入数据的长度或范围,并在必要时进行边界检查。
字符串操作类:
1、strcpy_s()
三个参数:指向目标字符串数组的指针 目标数组大小包括终止字符'\0' 指向源字符串的指针
errno_t strcpy_s(char *dest, size_t destSize, const char *src);
//char arr[30] = { 0 };//当目标为数组时,strcpy_s中间长度可以省略。
//strcpy_s(arr, "123456");
//return 0;
char * p = new char[64];//当目标为指针时,strcpy_s中间长度不可以省略。
strcpy_s(p, 64, "123456");
return 0;
返回值:在成功时返回 0,在失败时返回非零值。
2、strcat_s()
三个参数:指向目标字符串数组的指针 目标数组大小包括终止字符'\0' 指向源字符串的指针
errno_t strcat_s(char *dest, size_t destSize, const char *src);
返回值:在成功时返回 0,在失败时返回非零值。
九、命名空间
命名空间(Namespace)是一个封装标识符(变量、常量、函数、结构体、类、模板。)的区域。
作用:避免命名冲突。
特点:1、允许不连续。相同命名空间名称的定义,可以存在多个,可以在同一个源文件中,也可以在不同的源文件中,都属于同一个命名空间。
namespace ABC{
int a;
void test() {
cout << "test" << endl;
}
}
namespace ABC {
void test1() {
cout << "test1" << endl;
}
}
using namespace ABC;
int main()
{
a = 10;
cout << a << endl;
test();
test1();
return 0;
}
2、允许嵌套。
namespace ABC {
int a;
void test() {
cout << "test" << endl;
}
namespace A1 {
void test() {
cout << "ABC::A1::test" << endl;
}
}
}
//using namespace ABC;
//如果取消注释using namespace ABC::A1; 那么main函数中的test();调用将变得模糊。
using namespace ABC::A1;
int main() {
ABC::a = 10;
test();
return 0;
}
命名空间的两种使用方式:
1、使用命名空间名称::标识符的方式来访问。
#include "stdafx.h"
#include <iostream>
using namespace std;
namespace ABC{
int a;
void test() {
cout << "test" << endl;
}
}
int main()
{
ABC::a = 10;
cout << ABC::a << endl;
ABC::test();
return 0;
}
2、 使用命名using namespace 命名空间名称;的方式作前置声明,在声明之后,可以直接使用标识符来访问。
#include "stdafx.h"
#include <iostream>
using namespace std;
namespace ABC{
int a;
void test() {
cout << "test" << endl;
}
}
using namespace ABC;
int main()
{
a = 10;
cout << a << endl;
test();
return 0;
}
十、面向对象
1、类
类是具有相同属性和行为的一组对象的集合
创建一个汽车类,并实例化一个对象。程序如下:
class Car {
private://私有访问权限,类的内部可以访问,外部不允许访问
char name[32]; //成员变量
char color[32];
public://公共访问权限
void Set(const char *n, const char * c) { //成员函数
strcpy_s(name, n); //成员函数可以直接访问成员变量,遵循最近嵌套原则。
strcpy_s(color, c);
}
void Start() {
cout << color << name << "启动了!" << endl;
}
void Stop() {
cout << color << name << "熄火了!" << endl;
}
};
int main() {
Car c1; //定义对象 或者称 实例化对象
c1.Set("小米","蓝色");
c1.Start();
c1.Stop();
Car* p = &c1;//指针使用“指针变量->成员名”的方式访问public属性的成员。
p->Start();
p->Stop();
return 0;
}
1、类的规范
类名:大驼峰命名(每个字母开头大写)
class CarStudent{
}
类文件:包括头文件和源文件,头文件名字:类名.h; 源文件名字:类名.cpp
成员函数:小驼峰命名 (开头小写,第二个字母开始开头大写)
void setName(){
}
成员变量:常规以m_开头加变量名。
2、面向对象的四个基本特征
抽象 | 对具体对象问题概括,抽象这一类对象的公共性质并描述过程 |
封装 | 将抽象出的属性成员、行为成员相结合,将他们视为一个整体 |
继承 | 对具有层次关系的类的数据和操作进行共享的一种方式(声明派生类) |
多态 | 同一名称,不同的功能实现方式(重载函数、虚函数和模板) |
十一、函数参数的默认值
两点:第一放在声明的位置;第二从右缺省
放在声明位置两种情况:
1、在头文件中声明,在.cpp中定义,在主函数中调用
2、在主函数上方声明,在主函数下方定义成,在主函数中调用。
十二、内联函数(关键字inline)
思想:将函数调用展开到调用的位置,放弃函数调用,提高程序的执行效率。
十三、构造函数
构造函数的作用是:初始化成员变量。
构造函数的概念:1、与类同名。2、无返回值。3、自动调用。
1、编写一个书籍类
1)有成员变量:书名、价格、作者姓名
2)有成员函数:input可以实现输入功能,output可以实现打印成员变量数据
3)有无参构造函数,可以对成员变量进行初始化。
4)有有参构造函数,可以通过参数对成员变量进行初始化。
#include "stdafx.h"
#include <iostream>
#include <cstring>
using namespace std;
class Book {
private:
int price;
char bookName[50];
char author[30];
public:
Book(){//无参构造函数
price = 59;
strncpy_s(bookName,"Example Book", sizeof(bookName) - 1); // 使用strncpy防止溢出,并确保以'\0'结尾
bookName[sizeof(bookName) - 1] = '\0'; // 确保字符串以'\0'结尾
strncpy_s(author, "Sam", sizeof(author) - 1);
author[sizeof(author) - 1] = '\0';
}
Book(int a,char*p,char *q) {//有参构造函数
price = a;
strncpy_s(bookName, p, sizeof(bookName) - 1);
bookName[sizeof(bookName) - 1] = '\0';
strncpy_s(author, q, sizeof(author) - 1);
author[sizeof(author) - 1] = '\0';
}
void input() {
/*price = a;
strcpy_s(bookName, p);
strcpy_s(author, q);
*/
cout << "请输入价格、书名以及作者:" << endl;
cin >> price;
cin >> bookName;
cin >> author;
}
void output() {
cout << "书名是:" << bookName << endl;
cout << "作者是:" << author << endl;
cout << "价格是:" << price << endl;
}
};
int main()
{
Book b1;//无参构造函数调用
b1.output();
Book b2(30, "Great Novel", "John Doe");//有参构造函数调用
b2.output();
Book b3;
b3.input();//输入书名、作者、价格
b3.output();
return 0;
}
要求用定义一个圆形类Circle,其中包含如下成员:
1) 1个私有数据成员(半径)。
2) 3个公有函数成员(设置半径、计算面积、计算周长)。
3) 3个构造函数(不带参数的构造函数、带参数的构造函数和拷贝构造函数)
#include "stdafx.h"
#include <iostream>
#include <cstring>
#define PI 3.1415
using namespace std;
class Circle{
private:
double r;
public:
Circle() {//不带参
r = 5.5;
}
Circle(double r1) {//带参
r = r1;
}
Circle(Circle & c1){//拷贝构造参数
r = c1.r;
}
void setR(){
cin >> r;
}
void setS() {
double S;
S = PI*r*r;
cout << "圆的面积是" << S << endl;
}
void setC() {
double C;
C = 2 * PI*r;
cout << "圆的周长是" << C << endl;
}
};
int main()
{
Circle c1;
c1.setS();
c1.setC();
Circle c2(6.5);
c2.setS();
c2.setC();
Circle c3(c1);
c3.setS();
c3.setC();
return 0;
}
构造函数根据参数分为:
无参构造函数
有参构造函数
拷贝构造函数:作用是从一个已有对象初始化新建对象。
class A
{
int a;//类中的成员变量未声明权限默认为private权限
int b;
public:
A() {//无参构造函数
a = 0;
cout << "A()" << endl;
}
A(int n) {//有参构造函数
a = n;
cout << "A(int n)" << endl;
}
A(int n,int m) {//可提供多个构造函数,但是参数类型和数量不能完全一致
a = n;
b = m;
cout << "A(int n)" << endl;
}
A(const A&other) {//拷贝构造函数
cout << "拷贝构造" << endl;
}
};
/*对象和构造函数的调用次数是一致的。*/
int main(){
A a1; //这句命令两个作用:1、分配空间 2、调用构造函数
A a2(3);
A a3(3, 4);
/*以同类型的已有对象初始化新建对象时,调用的构造函数是拷贝构造函数,*/
A a4 = a1;//调用拷贝构造函数
A a5(a1);//调用拷贝构造函数
}
默认构造函数有三种场景:
1、系统自动生成。
2、无参构造函数。
3、所有参数都有默认值。
注意:理论上可以有多个默认构造函数,但是由于系统会出现调用不明确问题,因此最多只能有一个构造函数。
同时是可以存在无默认构造函数的情况,这种情况下无法直接创建一个数组对象。需要动态分配并初始化数组。
1、实现一个学生类
1)有成员变量:name, age,
2)有无参构造函数,完成初始化功能
3)成员函数input()完成输入功能
4)成员函数output()完成输出功能, 其他函数可自己根据需要定义。
在主函数中,要求用户输入学生人数,根据输入的学生数,动态创建学生对象,按年龄升序排序后,输出结果。
#include "stdafx.h"
#include <iostream>
using namespace std;
class Student {
public:
int age;
char name[30];
Student() {//无参构造函数
age = 18;
strcpy_s(name, "luzhiyu");
}
Student(int a,char *p) {//含参构造函数
age = a;
strcpy_s(name, p);
}
void intput() {
cout << "请输入学生的姓名和年龄:" << endl;
cin >> age;
cin >> name;
}
void output() {
cout << name << endl;
cout << age << endl;
}
/*
int getAge() {
return age;
}
*/
};
int main()
{
int n = 0;
cout << "请输入学生数量:" << endl;
cin >> n;
Student *p = new Student[n];
for (int i = 0; i < n; i++) {
p[i].intput();
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (p[j].age > p[j + 1].age) {
Student temp = p[i];
p[i] = p[i + 1];
p[i + 1] = temp;
}
}
}
for (int i = 0; i < n; i++) {
p[i].output();
}
delete[] p;
return 0;
}
十四、析构函数
作用;释放成员变量
特点:1、无参数、无返回值2、自动调用3、有且只有一个析构函数
格式 :
~ 类名(){
}
#include "stdafx.h"
#include <iostream>
using namespace std;
class A
{
public:
A(const char *str) {
int len = strlen(str) + 1;
p = new char[len];
strcpy_s(p, len, str);
}
void Output() {
cout << p << endl;
}
~A() {
delete[]p;
cout << "析构" << endl;
}
private:
char *p;
};
int main() {
//A a1("ABC");
//a1.Output();
A *p = new A("ABCD");
delete p;//两个作用,1是调用析构函数,2是释放内存
return 0;
}//对象在离开作用域,会自动调用析构函数
主函数中A *p = new A("ABCD")是在堆上重新分配空间并将ABCD放进空间中
delete p;//两个作用,1是调用析构函数释放A,2是释放内存ABCD.
十五、拷贝构造函数
1、无需实现拷贝构造函数
class File
{
public:
File(const File &) = delete;
File(const char * filename) {
fopen_s(&fp, filename, "w");
}
~File() {
fclose(fp);
}
void write(const char * str) {
fprintf(fp, "%s\n", str);
}
private:
FILE * fp;
};
int main() {
File f1("1.txt");
{
File f2 = f1;
f2.write("1234");
}
f1.write("ABC");
return 0;
}
2、需要实现拷贝构造函数
class String
{
public:
String(const char * s) {
int len = strlen(s) + 1;
str = new char[len];
strcpy_s(str, len, s);
}
void output() {
cout << str << endl;
}
~String() {
delete[]str;
}
String(String&other) {
int len = strlen(other.str) + 1;
str = new char[len];
strcpy_s(str, len, other.str);
}
private:
char * str;
};
void main() {
String s1("小明");
{
String s2(s1);
s2.output();
}
}
3、无法实现拷贝构造函数(默认无法使用,且自己写起来很麻烦,放弃使用拷贝构造函数)
class A
{
public:
A(int n) {
a = n;
}
void output() {
cout << a << endl;
}
private:
int a;
};
int main()
{
A a1(10);
A a2 = a1;
a2.output();
return 0;
}
十六、常成员函数
作用:不能修改成员变量
class A
{
public:
A(int n) {
a = n;
}
void update(int n) {
a = n;
}
void output() const {//常成员函数,表示该成员函数不会修改任何成员变量的值,让这个成员函数能够在常量对象上被调用。
cout << a << endl;
}
private:
int a;
};
int main()
{
const A a1(10); //创建一个常量MyClass对象
//a1.update(13);
a1.output();//可以调用output函数,因为output是const成员函数
return 0;
}
十七 、成员变量初始化的三种方式
1、构造函数体中
2、构造函数的初始化列表(主要解决对象初始化问题)
3、声明成员变量时
class A
{
public:
A():a2(2),a3(3)//构造函数的初始化列表优先级高于函数体中初始化
{
a1 = 1;
}
void output()const
{
cout << "a1=" << a1 << endl;
cout << "a2=" << a2 << endl;
cout << "a3=" << a3 << endl;
cout << "a4=" << a4 << endl;
}
private:
int a1;
int a2, a3;
int a4 = 4;//声明成员变量时初始化
};
构造函数的初始化列表(主要解决对象初始化问题)
首先构造了 A
类的成员 b1
(类型为 B
),使用传递给 B
构造函数的参数 1
,然后输出了 1
。接着,A
类的构造函数体被执行,输出了 "A"
。
class B
{
public:
B(int n) {
cout << n << endl;
}
};
class A
{
public:
A() :b1(1)
{
cout << "A" << endl;
}
private:
B b1;
};
int main()
{
A a;
}
成员变量初始化的顺序
1、成员变量的初始化顺序和成员变量声明顺序相关。先声明,先初始化。
这里a先声明,但是a的初始化值无法确定,因此会随机初始化一个值。
class Test
{
public:
Test(int n) :b(n), c(b + 1), a(c)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
private:
int a;
int b;
int c;
};
int main() {
Test t(2);
return 0;
}
2、先调用对象成员所属类的构造函数,再执行自身类的函数体。
创建Ex类的对象e,先调用Ex类构造函数,通过构造函数初始化列表,调用Test构造函数,
e在生命周期结束时先调用Ex析构函数,执行完毕,自动调用test的析构函数
class Test
{
public:
Test(int n) {
cout << n << endl;
}
~Test() {
cout << "~test" << endl;
}
};
class Ex
{
Test test;
public:
Ex() :test(10) {
cout << "ex" << endl;
}
~Ex() {
cout << "~ex" << endl;
}
};
int main() {
Ex e;
}
十八、C++中类和结构体区别/字节对齐;
1、C++结构体和类的主要区别在于默认访问权限:结构体成员默认是公有的(public),而类成员默认是私有的(private)。
struct A
{
A() {
cout << "A" << endl;
}
~A() {
cout << "~A1" << endl;
}
void output()const {
cout << "1" << endl;
}
};
int main() {
A a1;
a1.output();
}
2、类的字节对齐和结构体一样
1、结构体各成员的起始位置相对与结构体变量的起始位置的偏移量应该是这种类型的倍数。
2、结构体变量占总字节数应该是结构体各成员最大类型所占字节数的倍数。
注意:空类所占内存为1个字节。
十九、this关键字
参数和成员变量名称相同,就必须要使用this指针来加以区别
this关键字的作用是:指向当前对象。
this指针是一个隐含的指针,它是指向对象本身,代表了对象的地址。
静态成员函数没有this指针。
class Student
{
public:
Student(const char *name, int age)
{
this->age = age;
strcpy_s(this->name, name);
}
void output()const {
cout << name <<" "<< age << endl;
}
private:
char name[30];
int age;
};
int main() {
Student s1("li", 15);
s1.output();
return 0;
}
二十、静态成员
静态成员包括静态成员变量和静态成员函数
作用:为了实现一个类的不同对象之间的数据和函数共享。
static关键字用在:变量:静态全局变量
静态局部变量
静态成员变量
函数:静态全局函数
静态成员函数
1、静态成员变量
要求:类外初始化。
语法:类型 类名::静态成员变量=数值/表达式
int A::no = 0;
访问方式有两种:
对象.静态成员变量
类名::静态成员变量
2、静态成员函数
静态成员函数只能直接访问该类的静态成员变量、静态成员函数以及类以外的数据和函数,而访问非静态成员必须通过参数传递方式得到类的对象,然后通过对象名来访问。
语法:
class 类名
{
访问权限:
static 返回值 函数名(形参列表);
};
访问方式:
在类内可以直接通过函数名称访问。
在类外可以通过 类名::静态成员函数(实参)
对象.静态成员函数(实参)
指针->静态成员函数(实参) 来访问。
示例代码:
class Point
{
int x, y;
public:
static int no;
Point(int a, int b) :x(a),y(b)
{
++no;
}
static void print()
{
cout << no << endl;
}
};
int Point::no = 0;//要求类外初始化,初始化格式:数据类型 类名::静态成员变量=初始化值
void main() {
Point p(1, 10);
cout << p.no << endl;//访问两种方式:对象.静态成员变量
cout << Point::no << endl;//类名::静态成员变量
Point *p2 = new Point(3, 3);
p2->print();
Point::print();
}
二十一、友元
作用:打破封装。(不考虑权限)
友元函数
1、友元函数不是当前类的成员函数,但是可以访问该类所有成员。
2、友元函数的本质是全局函数,不考虑位置在哪。友元函数的原型说明位置可以在类的任何位置。
3、友元函数定义在类体内,只需在函数返回值类型前加friend。
友元函数的定义在类体外,只需要将加有friend的函数原型说明放在类体内。
class A
{
private:
int a;
friend void Output(A&o);
public:
A(int x) :a(x) {}
friend void Test(A&o)
{
cout << "Test调用" << o.a << endl;
}
};
void Output(A&o)
{
cout << o.a << endl;
}
int main()
{
A t(100);
Output(t);
Test(t);
return 0;
}
友元类
友元关系不双向、不传递、不被继承。
#include "stdafx.h"
#include <iostream>
using namespace std;
class A {
private:
int a, b;
friend class B;//B是A的友元类,则B的所有成员函数都是A类的友元函数,可以访问A类的所有成员
public:
A() {
a = 1;
b = 10;
}
};
class B {
public:
void output(A&a) {
cout << "a=" << a.a
<< "b=" << a.b
<< endl;
}
};
void main() {
A a;
B b;
b.output(a);
return;
}
友元成员函数
#include "stdafx.h"
#include <iostream>
using namespace std;
class A;
class B { //将B::output声明为A的友元,需要提前声明。
//B类尚未定义会导致编译错误。
public:
void output(A& a);
};
class A {
friend void B::output(A& a); //友元成员函数不是当前类的成员函数,但是可以访问该类所有成员
private:
int a, b;
public:
A() {
a = 1;
b = 10;
}
};
void B::output(A&a) {
cout << "a=" << a.a
<< " b=" << a.b
<< endl;
}
void main() {
A a;
B b;
b.output(a);
}
二十二、多态
多态包括静态多态(编译时多态)和动态多态(运行时多态)两种,静态多态有函数重载、模板,动态多态是虚函数。
二十三、函数重载
1、函数重载的定义:
在同一个作用域内,可以有一组相同的函数名,不同参数列表的函数,这组函数被称为重载函数。
2、函数重载的作用:
达到行为标识符统一,减少程序中标识符的个数
3、判断函数重载的依据是:函数名称、参数列表(参数类型或者个数)。
#include "stdafx.h"
#include <iostream>
using namespace std;
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
char * add(char*a, char *b) {
strcat_s(a, 100, b);
return a;
}
void main() {
char str[100] = { 0 };
strcpy_s(str, "hello,world");
cout << add(str, "小明") << endl;
cout << add(10, 20) << endl;
cout << add(10.5, 8.9) << endl;
}
4、const关键字可以用于参与对重载函数的区分
常成员函数不能修改成员变量的值,也不能调用该类中其他没有用const修饰的成员函数。
#include "stdafx.h"
#include <iostream>
using namespace std;
class S {
int a;
public:
S(int x):a(x){}
void print() { //非常成员函数
cout << a << endl;
}
void print()const { //常成员函数 ,二者为重载成员函数
cout << "const" << a << endl;
}
};
void main() {
S a(10);
a.print(); //调用非常成员函数
const S& a1 = a;
a1.print(); //调用常成员函数
}
二十四、运算符重载
1、运算符重载必须定义一个函数,有两种实现方式:成员函数和友元函数,运算符的操作数通常也应该是类的对象。
2、双目运算符
当使用成员函数实现时,有一个参数,运算符左侧为当前对象,右侧为参数。
当使用友元函数时,由于不是类的成员函数,没有this指针。参数表有两个参数
3、单目运算符
当使用成员函数实现时,没有参数,特殊情况(++、--)除外。
当使用友元函数时,参数表中有一个参数
4、二元运算符重载