C++

53 阅读15分钟

C到C++

  • linux系统中c语言用gcc运行.c文件,c++用g++运行.cpp文件
  • 描述 c++=C+C++标准库
  • C++完全兼容C
  • C++是一门强类型的编程语言:类型检查严格
  • C++有一些优化:struct class union 三个关键字使用可以省略
  • c中bool类型是枚举,需要 添加头文件stdbool.h,c++中bool、true、false是关键字
  • c++如果要使用c的头文件,就把后缀.h改为把c放在头文件名称的第一个字母
  • 例如:#include 就是 #include
  • 注意 cstring 和 string是两个东西,前者是C语言的头文件,后者是c++的头文件(使用string定义字符串要引入)

函数的缺省参数

  • 个人理解,就是定义一个默认参数,调用函数没传参时,使用这个默认参数
  • c语言中函数的形参的值必须等到调用函数的时候,实参赋值给形参
  • C++中可以自声明的时候就设置一个缺省(默认)参数,如果没有实参赋值给形参,形参的值就是实参的值。
#include <stdio.h>

/**
 * 判断一个整数是否为素数
 * 
 * @param a 需要判断的整数
 * @return 返回1表示是素数,返回0表示不是素数
 */
int isSuShu(int a)
{
    int flag = 1; // 默认假设a是素数

    // 遍历从2到a-1的所有整数,检查是否能整除a
    for (int i = 2; i < a; i++)
    {
        if (a % i == 0)
        {
            flag = 0; // 如果能整除,则a不是素数
        }
    }
    
    return flag;
}

/**
 * 打印从1到指定数之间的所有素数
 * 
 * @param num 指定的上限数,默认为100
 */
void SuShu(int num = 100)
{
    // 遍历从1到num-1的所有整数,判断是否为素数并打印
    for (int i = 1; i < num; i++)
    {
        if (isSuShu(i))
        {
            printf("%d是素数\n", i);
        }
    }
}

int main()
{
    int a;
    scanf("%d", &a); // 从用户输入读取一个整数

    SuShu(a); // 打印从1到a之间的所有素数
    SuShu();  // 打印从1到100之间的所有素数(使用默认参数)

    return 0;
}

语法规定

  1. 只能在声明的时候写,不能在定义的时候写
  2. 从右往左,缺省参数必须放在最后面
  3. 调用的时候是根据类型来区分的
  4. 和函数重载一起使用时,注意二义性问题

函数重载

  • 个人理解,函数可以重名,并且不会相互覆盖,会通过你调用函数时传入入参的个数,找到与之参数对应的函数
  • 我们写代码的时候,函数名字一样,参数列表不一样,在编译成机器语言后,会变成不一样的函数

参数列表不同

  • 参数个数不同
  • 参数类型不同
  • 参数顺序不同
  • const修饰基本数据类型和不修饰一样,const修饰指针和指针不一样

名字空间 namespace

  • 个人理解,就是一块单独作用域,可以用来定义一些变量、函数、方法这些调用的时候要先暴露才能用
  • 又叫命名空间
  • 为了多人协同方便区分标识符,创建了名字空间,每个人可以有单独的名字空间,名字空间内的标识符不能一样,不同名字空间里的标识符一样可以通过名字空间来区分。

使用

// 定义
namespace mingZiKongJin{ 
    int n=6; 
}
// 临时使用 
printf("%d", mingZiKongJin::n); 
// 暴露名字空间中的某个名字,以后都持续使用


// 暴露整个空间,里面的以后都可以持续使用



输入输出流(io流) iostream

  • 相当于c的scanf和printf

动态内存分配

  • c语言的动态内存分配是malloc、free
  • c++可以使用c的动态内存分配,新增运算符new、delete
  • new 搭配delete
  • new []搭配delete[]

结构体和this指针

  • c++结构体里可以定义函数
  • 结构体里的函数,通过this.来访问结构体里的其他变量

类与对象

面向对象编程思想 oop

  • 编程 == 数据结构 + 算法
  • 数据结构 == 对象 物
  • 算法 == 流程 事 成员函数 功能 方法

面向过程编程思想

  • 核心是过程
  • 描述事物的时候,以流程为核心 数据是附属于流程的
  • 实时性要求高,开发团队小。代码复用要求不高,代码量小。

面向对象过程思想

  • 描述事物的时候以对象为核心,流程是依附于对象的。
  • 实时性要求低,开发团队大,代码复用要求高,代码量大。

面向对象特性

四大特性:

  • 抽象
  • 封装
  • 继承
  • 多肽

抽象

  • 分类的类
  • 类是特性的集合
  • 特性包括:
  • 属性 数据 用成员变量来描述
  • 功能 流程 用成员函数来描述
  • 是虚的
对象
  • 现实世界:先有对象,再抽象出类
  • 数字世界:先定义类,再用类构造出对象
  • 先有类,再有对象
  • c++:支持面向对象,允许用面向过程的思维来解决问题,也允许用面向对象的思维来解决问题。
  • 纯面向对象:只允许用面向对象的思维来解决问题。万物皆对象。
class
  • class是c++中用来声明的关键字。
  • c++中class和struct的区别仅仅在于:
  • class里的成员默认私有,struct里的成员默认公有。如果不封装的话,class和struct没有区别

封装

为了对象被创建出来之后,队友不会随便改变某个属性,就有了封装特性。

#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
class Hero
{
	string name;
	int        ph;//血量
	int        bh;//蓝条
public:
	// 函数声明
	void init(string name, int ph, int bh);
	// 函数声明
	void show();
	//接口函数
	void setPh(int p){ ph = p; }
	int getBh(void){ return bh; }

	void hit(Hero* pHero){
		cout << name << "向" << pHero->name << "打出一招降龙十八掌,耗费10点魔法,打掉"
			<< pHero->name << "100点血" << endl;
		bh -= 10;
		pHero->ph -= 100;
	}
};


/**
 * @brief Hero::init给hearo类定义的成员函数 初始化英雄对象的属性
 * 
 * 该函数用于设置英雄的名字、血量和魔法值。
 * 
 * @param name 英雄的名字
 * @param ph 英雄的血量
 * @param bh 英雄的魔法值
 */
void Hero::init(string name, int ph, int bh)
{
	this->name = name;
	this->ph = ph;
	this->bh = bh;
}

/**
 * @brief Hero::show给hearo类定义的成员函数 显示英雄的当前状态
 * 
 * 该函数用于在控制台输出英雄的名字、血量和魔法值。
 */
void Hero::show(){
	cout << "名字:" << name << endl;
	cout << "血量:" << ph << endl;
	cout << "魔法:" << bh << endl;
}

int main(){
	Hero h1,h2;
	h1.init("张无忌", 1000, 100);
	h2.init("张三丰", 2000, 50);


	h1.show();
	h2.show();
	cout << "-------------------------------" << endl;
	h1.setPh(500);
	cout << h2.getBh() << endl;
	h1.show();
	h2.show();
	cout << "-------------------------------" << endl;
	h1.hit(&h2);
	cout << "-------------------------------" << endl;
	h1.show();
	h2.show();
	cout << "-------------------------------" << endl;
	while (1);
	return 0;
}

公有 关键字 public
  • public修饰的成员,可以通过对象名来访问。就像结构体一样。
私有 关键字 pivate
  • private修饰的成员,只能对象的成员访问。不能通过对象名访问。
  • 函数中可以访问类的私有成员。
  • 如果希望访问一个对象

构造析构

  • 构造函数 构造器 对应对象的诞生
  • 析构函数 析构器 对应对象的销毁
构造函数
  • 每个对象都是个构造函数构造出来的。
  • 如果我们定义类的时候没有自定义构造函数,编译器就会帮我们写一个构造函数。这个构造函数因为是缺省定义的,所以叫做缺省构造函数。缺省构造函数没有参数。
  • 构造函数名和类名一致。
  • 构造函数没有返回类型。
  • 构造函数可以有任意个参数。
构造函数重载
  • 一个类可以有多个构造函数,构成重载
  • 一个类的不同对象可以由不同的构造函数构造而来
  • 一个对象有且只有一个构造函数。

析构函数

  • 在对象被销毁的时候自动被调用
  • 构造函数在对象创建的时候自动被调用
  • 析构函数没有返回类型名

C++Day2

类的特殊成员

const

复习C语言中的const

  • const修饰普通变量
  • 变了只能读访问,不能写访问,必须初始化
const 修饰指针

指针常量:const作用于p,p是常量,*p是变量

常量指针:const 作用于 *p,p是变量,*p是常量

const修饰类里面的值属性
  • 语法 const int a;
  • 赋值的时候,在构造函数中对其赋值需要使用自动传参列表student(aValue):a(aValue){ };
  • 自动传参列表只能在定义的时候写,不在声明的时候写
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;

/**
 * @class student
 * @brief 学生类,包含学生的ID、姓名和成绩信息。
 */
class student
{
private:
    const int id;        // 学生的唯一标识符,不可修改
    const string name;   // 学生的姓名,不可修改
    int score;           // 学生的成绩,可以修改

public:
    /**
     * @brief 构造函数,初始化学生对象。
     * @param id 学生的唯一标识符
     * @param name 学生的姓名
     * @param score 学生的成绩
     */
    student(int id, string name, int score);

    /**
     * @brief 析构函数,释放学生对象的资源。
     */
    ~student();

    /**
     * @brief 显示学生的ID、姓名和成绩信息。
     */
    void show()
    {
        cout << "id:" << id << ",ÐÕÃû:" << this->name << ",³É¼¨:" << this->score << endl;
    }
};

/**
 * @brief 构造函数实现,初始化学生对象的ID、姓名和成绩。
 * @param id 学生的唯一标识符
 * @param name 学生的姓名
 * @param score2 学生的成绩
 */
student::student(int id, string name, int score2) : id(id), name(name)
{
    score = score2;
}

/**
 * @brief 析构函数实现,释放学生对象的资源。
 * @note 由于id和name是const类型,delete操作在此处是不必要的,且会导致未定义行为。
 */
student::~student()
{
    delete &id;
    delete &name;
    delete &score;
}

/**
 * @brief 主函数,创建并显示多个学生对象的信息。
 * @return 程序执行成功返回0
 */
int main()
{
    student s1(1, "zhangsan", 100);
    s1.show();
    student s2(2, "lisi", 90);
    s2.show();
    student s3(3, "wangwu", 80);
    s3.show();
    return 0;
}
const修饰类里面的函数
  • 语法:int getscore() const { return score; }
  • 修饰函数内不得改变成员变量
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;

/**
 * @class student
 * @brief 学生类,包含学生的ID、姓名和成绩信息。
 * 
 * 该类用于存储和展示学生的基本信息,包括ID、姓名和成绩。
 */
class student
{
private:
    const int id;        // 学生的ID,不可修改
    const string name;   // 学生的姓名,不可修改
    int score;           // 学生的成绩,可以修改

public:
    /**
     * @brief 构造函数,初始化学生对象。
     * @param id 学生的ID
     * @param name 学生的姓名
     * @param score 学生的成绩
     */
    student(int id, string name, int score);

    /**
     * @brief 析构函数,释放学生对象的资源。
     */
    ~student();

    /**
     * @brief 显示学生的ID、姓名和成绩。
     */
    void show()
    {
        cout << "id:" << id << ",姓名:" << this->name << ",成绩:" << this->score << endl;
    };

    /**
     * @brief 获取学生的成绩。
     * @return 学生的成绩
     */
    int getscore() const
    {
        return score;
    }
};

/**
 * @brief 构造函数实现,初始化学生对象的ID、姓名和成绩。
 * @param id 学生的ID
 * @param name 学生的姓名
 * @param score2 学生的成绩
 */
student::student(int id, string name, int score2) : id(id), name(name)
{
    score = score2;
}

/**
 * @brief 析构函数实现,释放学生对象的资源。
 */
student::~student()
{
    delete &id;
    delete &name;
    delete &score;
}

/**
 * @brief 主函数,创建并展示多个学生对象的信息。
 * @return 程序执行成功返回0
 */
int main()
{
    // 创建学生对象s1,并展示其信息
    student s1(1, "zhangsan", 100);
    s1.show();

    // 创建学生对象s2,并展示其信息
    student s2(2, "lisi", 90);
    s2.show();

    // 创建学生对象s3,并展示其信息
    student s3(3, "wangwu", 80);
    s3.show();

    // 获取并展示s3的成绩
    int value  = s3.getscore();
    cout << "s3的分数是:" << value << endl;

    return 0;
}

static

C里的static
  • 修饰变量为静态变量,作用域还是不变,生命周期不会随着函数的消亡而消亡,而是等到程序结束才消亡。
  • 限制extern拓展作用域,extern可以把同一个工程中其他文件中的变量作用域拓展到当前文件。
  • static就是限制某个变量作用域不能被拓展到其他文件中
类的静态成员变量

语法在类里面 static int n; 类的静态成员必须在类外初始化 int student::total = 0;

  • 个人理解,就是类会创造一个所有类创建的变量都共用的一部分数据,类的静态成员变量属于整个类,不属于类的某一个对象,无论是那个对象修改这个静态成员变量,其他对象去访问这个静态成员变量,都是修改后的值。
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;

class student
{
private:
    const int id;
    const string name;
    int score;
    static int total; // 学生总数

public:
    student(int id, string name, int score, int totalValue);
    ~student();
    void show()
    {
        cout << "id:" << id << ",姓名:" << this->name << ",成绩:" << this->score << ",学生总数:" << this->total << endl;
    };
    int getscore() const
    {
        return score;
    }
};

student::student(int id, string name, int score2, int totalValue) : id(id), name(name)
{
    score = score2;
    total = totalValue;
}

student::~student()
{
    delete &id;
    delete &name;
    delete &score;
}
int student::total = 0; // 初始化静态变量值
class teacher
{
private:
public:
    static teacher *teacherFn()
    {
        static teacher teacher;
        return &teacher;
    }
};
int main()
{
    teacher *p = teacher::teacherFn();
    teacher *p2 = teacher::teacherFn();
    cout << p << "," << p << endl;
    return 0;
}
类的静态成员函数

语法 static void test() { cout << n << endl; // 只访问静态成员变量 n } 只能通过类名调用静态成员函数 A::test();,用类生成的对象调用不了

  1. 类的静态成员函数只能访问类的静态成员,不能访问类的非静态成员。
  2. 类的静态成员函数可以通过类名来访问,类的非静态成员函数不能通过类名来访问。
  3. 如果一些类只有功能,可以考虑设置为静态成员函数
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;

class A
{
public:
    int m;
    static int n;
    A() : m(0) {} // 初始化成员变量 m
    void set(int m, int n)
    {
        this->m = m;
        this->n = n;
    }
    void show()
    {
        cout << "当前对象的n:" << n << ",m:" << m << endl;
    }
    static void test()
    {
        cout << n << endl; // 只访问静态成员变量 n
    }
};
int A::n = 1; // 类外初始化

int main()
{
    A a, b;
    A::test(); // 通过类名调用静态成员函数
    a.show();  // 通过对象实例调用非静态成员函数

    return 0;
}

引用

  • C语言里面讲过引用,动词,意思是使用。引用变量等同于使用变量绑定的内存段。
  • C++里面新增的引用是名字。是一种新的类型。是内存段的别名。
  • 引用本身不占据内存段,必须初始化。
  • 语法:int m = 10; int& n = m;

引用和指针的区别

  1. 指针占内存,引用不占内存
  2. 指针可以改变指向,引用不能
  3. 引用必须初始化,指针不必
  4. 指针可以改变类型,引用不能
  5. 有空指针,没有空引用 共同点:都可以指明某块内存区域,指针要解引用,引用就是直接操作。都有类型。 引用的大小是内存段的大小,指针的大小恒为4(32位计算机)或者8(64位计算机)

拷贝构造

个人理解,类有三个构造器,一个有参构造,一个无参构造,一个拷贝构造

  • 例如类名为student,public里的student()函数就是无参构造。
  • student(string name)传入参就是有参构造。
  • student(student &stuData)只有一个参数,并且参数类型就是这个类的话就是拷贝构造。
  • 拷贝构造需要用另一个已经声明的对象去赋值给一个新对象student s2 = s1;
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
// 无参构造
class A
{
public:
    A() {};
};

// 有参构造函数和拷贝构造函数
class student
{
public:
    const int id;
    const string name;
    int score;
    static int total; // 学生总数
    student(int id, string name, int score, int totalValue);
    student(const student &s) : id(s.id), name(s.name) // 拷贝构造函数
    {
        score = s.score;
        total = s.total;
        cout << "拷贝构造函数" << endl;
    };
    ~student();
};

student::student(int id, string name, int score2, int totalValue) : id(id), name(name)
{
    cout << "构造函数" << endl;
    score = score2;
    total = totalValue;
    total++; // 递增学生总数
}
int student::total = 0;
student::~student()
{
    total--; // 递减学生总数
}

int main()
{
    A b;
    student s1(1, "zhangsan", 100, 1);
    student s2 = s1;                   // 拷贝构造函数
    printf("s1.score:%d\n", s1.score); // 修正输出语句
    return 0;
}

运算符重载

个人理解,运算符重载就是将本来就有的+、-、*、/、%等自带的运算符,通过自定义的方式进行重写 比如c语言中的+不能将两个字符串相加,通过运算符重载的方式就可以自己自定义一个可以将字符串相加的+

友元(友缘)

  • 个人理解,就是当一个给一个类,在定义的时候关联一个类外面功能,可以是函数和类,让外面的函数或者类可以访问和修改类里面的变量
友元函数

在类的public内:friend void setScore(Student &student, int newScore);

#include <iostream>
#include <string>

using namespace std;

class Student
{
private:
    const string name; // 常量名字
    int age;
    int score; 
public:
    // 构造函数
    Student(string nameValue, int ageValue, int scoreValue) : name(nameValue) // :name(nameValue)自动传参列表
    {
        age = ageValue;
        score = scoreValue;
    }

    void show() const // const修饰的函数,不能改变成员变量,只读
    {  
        cout << "Name: " << name << ", Age: " << age
             << ", Score: " << score << endl;
    }

    // 友元函数:允许外部修改成绩
    friend void setScore(Student &student, int newScore);
};

// 定义友元函数:修改学生成绩
void setScore(Student &student, int newScore)
{
    student.score = newScore; // 直接访问student的私有属性 score,并修改
}

int main()
{
    Student student1("张三", 20, 60); // 名字在初始化后不可修改
    student1.show();                  
    setScore(student1, 90);           // 通过友元函数设置成绩
    student1.show();      
    return 0;
}
友缘类

在类的public内:friend class teacher;

#include <iostream>
#include <string>

using namespace std;

class Student
{
private:
    const string name; // 常量名字
    int age;
    int score;

public:
    // 构造函数
    Student(string nameValue, int ageValue, int scoreValue) : name(nameValue) // :name(nameValue)自动传参列表
    {
        age = ageValue;
        score = scoreValue;
    }

    // 友元类:允许外部查看修改成绩
    friend class teacher; // 将teacher类声明为友元类
};

class teacher
{
public:
    void getStudent(Student &student) const // const修饰的函数,不能改变成员变量,只读
    {
        cout << "Name: " << student.name << ", Age: " << student.age
             << ", Score: " << student.score << endl;
    }
    void setStudent(Student &student, int scoreValue)
    {
        student.score = scoreValue;
    }
};

int main()
{
    Student student1("张三", 20, 60); // 初始化
    teacher teacher1;
    teacher1.getStudent(student1);
    teacher1.setStudent(student1, 99);
    teacher1.getStudent(student1);
    return 0;
}