c++学习笔记
注:我是看视频敲的代码 视频学习链接 侵删 黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难
1、基本使用
#include<iostream>
#include <string>
using namespace std;
int *swap(int *a, int *b) {
int ARR[2];
int tem = *a;
*a = *b;
*b = tem;
ARR[0] = *a;
ARR[1] = *b;
return ARR;
}
void swap2(int &c,int &d) {
int tem = c;
c = d;
d = tem;
}
// 结构体
struct MyStruct
{
int age;
string name;
};
//结构体指针--嵌套结构体
struct MyStruct2
{
int age;
string name;
MyStruct s;
};
//引用 int &小名=原名
int main() {
int arr[3] = { 2,3,4 };
int a = 3;
int b = 4;
int *rr;
MyStruct s3[3];
MyStruct2* s4=new MyStruct2[2];
MyStruct2 *ssss = new MyStruct2[12];
s4->age = 13;
s3[0].age = 16;
s3[0].name = "计算机";
cout << s3[0].age << "姓名" <<s4->age<< s3[0].name << endl;
swap2(a, b);
cout << a << "-------" << b<< endl;
delete []s4;// 对应New 使用的数组 学习到引用的注意事项
//引用之前必须初始化,引用一旦初始化之后 就不可以更改引用
// 静态变量存放在全局区 static int s;存放的是全局区
}
2、引用的本质
-
引用的本质是指针常量 -
*相当于解引用,找到变量的地址
#include<iostream>
#include <string>
using namespace std;
void swap(const int &a){
a=1000;//将会报错,因为有const修饰
}
3、函数的提高
3.1 函数的默认参数
#include<iostream>
#include <string>
using namespace std;
int fun(int a,int b=10 ,int c=10) {
return a + b + c;
}
int main() {
cout << fun(10, 20, 30);//c传入参数就用传入的参数 否则就用默认的参数
// 从某个位置有了默认参数,则从左到右必须都要有
// 函数实现和声明只能一个有默认参数
}
3.1 函数的占位参数
#include<iostream>
#include <string>
using namespace std;
int fun(int a ,int) {//第二个参数就是占位符亦可以默认参数 int a=10
return a ;
}
int main() {
cout << fun(10, 20);//c传入参数就用传入的参数 否则就用默认的参数
// 从某个位置有了默认参数,则从左到右必须都要有
}
3.2函数的重载
作用:函数名可以相同,提高复用性
满足条件:
- 同一个作用域
- 函数名称相同
- 函数参数类型不同或者个数不同或者顺序不同
注意:函数的参数不可以作为函数重载的条件
#include<iostream>
#include <string>
using namespace std;
// 函数重载
void fun() {
cout << "函数重载fun()" << endl;
}
void fun(int a ) {// 函数参数个数不同
cout << "函数重载fun(int a)" << endl;
}
void fun(double a) {// 函数参数类型不同
cout << "函数重载fun(double a)" << endl;
}
void fun(double a,int b) {// 函数参数类型不同
cout << "函数重载fun(double a,int b)" << endl;
}
void fun(int a, double b) {// 函数参数顺序不同
cout << "函数重载fun(int b,double a)" << endl;
}
void fun(const int &a) {// 函数参数顺序不同
cout << "函数重载fun(const int b,double a)" << endl;
}
void fun(int &a ) {// 函数参数个数不同
cout << "函数重载fun(int a)" << endl;
}
//int fun() {
// cout << "函数返回值不可以重载fun()" << endl;
//}
int main() {
fun(a);//调用无const
fun(10);// 调用有const
fun();
fun(10);
fun(10, 0.1);
return 0;
}
// 函数重载遇到默认参数时就会报错 尽量避免这种情况
4.类和对象
#include<iostream>
#include <string>
using namespace std;
#define PI 3.14;
// 类和对象
class student
{
public:
//属性 成员属性 成员变量
string name;
string num;
void setName(string nnname) {
name = nnname;
}
// 行为-方法
void getData() {
cout << name << "----" << num << endl;
}
};
int main() {
//实例化对象
student s;
//s.name = "张三";
s.setName("张三");
s.num = "123321";
s.getData();
return 0;
}
4.1访问权限
#include<iostream>
#include <string>
using namespace std;
#define PI 3.14;
// 访问权限
class student
{
public://类内部可以访问 类外可以访问
string name;
void setname(string outcar) {
car = outcar;
cout << "可以访问" << endl;
show(outcar);
}
protected://类内部可以访问 类外不可以访问
string car;
void show(string ccc) {
cout << ccc<< endl;
}
private://类内部可以访问 类外不可以访问
int password;
};
int main() {
student s;
s.setname("宝马");
//s.show();//报错 外部不可以访问
return 0;
}
4.2 struct 和 class的区别
-
struct 默认权限 public -
class 默认权限 private
4.3成员属性私有化
#include<iostream>
#include <string>
using namespace std;
#define PI 3.14;
// 成员属性设置为私有
// 可以自己控制读写权限
// 对于写可以检测数据的有效性
// 写 -----set方法 读----get方法
class person
{
public:
void setName(string nname) {//写
name = nname;//传入参数和成员变量名称尽量不一样
}
void getName() {//读
cout << "姓名:" << name << endl;
}
void getAge() {//读
age = 0;
cout << "年纪:" << age << endl;
}
private:
string name;//可读可写
int age;//可读
string lover;//可写
};
int main() {
person p;
p.setName("xzs");
p.getName();
p.getAge();
//p.setAge();//年纪不可写 报错
return 0;
}
4.4对象的初始化和清理
- 构造函数:初始化 --主要用于创建对象时为对象成员赋值,由编译器自动调用 无需手动调用
- 析构函数:清理-对象销毁前系统自动调用,执行一些清理工作。
- 以上函数由编辑器自动调用,如果不写 编译器提供的是二者的空实现
#include<iostream>
#include <string>
using namespace std;
class MyClass
{
public:
MyClass(int a) {
age = a;
cout << "构造函数" << endl;
}
~MyClass() {
cout << "析构函数" <<age<< endl;
}
void setAge(int agge);
private:
int age;
};
void test(int a) {
MyClass c(10);
}
int main() {
test(10);
return 0;
}
4.5构造函数的分类以及调用
#include<iostream>
#include <string>
using namespace std;
class person
{
public:
person(int aage) {
cout << "有参构造" << endl;
}
person() {
cout << "无参构造函数" << endl;
}
person(const person& p) {
cout << "拷贝构造函数" << endl;
}
public:
int age;
};
int main() {
// 调用
person p;// 不可以写成person p();
person p2(10);
person p3(p2);
return 0;
}
4.3拷贝构造函数调用时机
- 使用一个已经创建完毕的对象初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
4.4g构造函数调用规则
默认情况下。c++编译器至少给一个类添加3个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
给猴枣函数调用规则如下:
- 如果用户定义有参构造函数,c++ 不提供默认无参构造函数,但是提供默认拷贝构造
- 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
4.5 深拷贝与浅拷贝
- 深拷贝: 在堆区重新申请空间进行拷贝操作
- 浅拷贝:简单的赋值拷贝操作
? new 关键字开辟的是堆区 需要手动释放 析构函数不能进行释放
#include<iostream>
#include <string>
using namespace std;
class person
{
public:
person(int a,int eight ) {
age = a;
height = new int(eight);
cout << "构造函数" << endl;
}
~person() {
// 浅拷贝带来的问题就是堆区内存的重复释放 需要利用深拷贝解决
if (height!=NULL)
{
delete height;
height = NULL;
}
cout << "p析构函数" << endl;
}
person(const person& p) {
age = p.age;
//height = p.height;//这是编译器自己默认实现的,需要自己进行深拷贝操作
height = new int(*p.height);//堆上面申请的空间
cout << "拷贝构造函数" << endl;
}
public:
int age;
int* height;//指针类型
};
void test() {
person p(10,90);
cout << "p的年龄" << p.age << endl;
person p2(p);
cout << "p2的年龄" << p2.age << endl;
}
int main() {
test();
return 0;
}
浅拷贝带来的问题就是堆区内存的重复释放 需要利用深拷贝解决
4.6 初始化列表
注意第八行冒号的位置
#include<iostream>
#include <string>
using namespace std;
class person
{
public:
person(int a, int b, int c) :age(a), height(b), money(c) {};
void show() {
cout << age <<height<< endl;
}
public:
int age;
int height;
int money;
};
int main() {
person p(1,2,3);// person p(1);===person p=1;===person p=person(1);构造方式一样 一般都用括号方式构造
p.show();
return 0;
}
4.7类的对象作为类的成员
#include<iostream>
#include <string>
using namespace std;
class phone
{
public:
phone(string name) :name(name) {
cout << "phone的构造方法" << endl;
}
~phone() {
cout << "phone的析构方法" << endl;
}
public:
string name;
};
class person
{
public:
// phone p="大哥大" ----- phone p("大哥大)
person(string prname, string phname):sname(prname),pname(phname) {
cout << "person的构造方法" << endl;
}
~person() {
cout << "person的析构方法" << endl;
}
public:
string sname;
phone pname;
};
void test() {
person p("张三","大哥大");
cout << p.sname << "拿着" << p.pname.name << endl;
}
int main() {
test();
return 0;
}
4.8 静态成员
- 静态成员变量
- 所有对象共享一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
- 静态成员函数
- 所有对象共享一个函数
- 静态成员函数只能访问静态成员变量
#include<iostream>
#include <string>
using namespace std;
class person
{
public:
static int a;//静态变量内部定义 内外初始化 见第11行
static void getA() {
cout << "a的结果为:" << a << endl;//静态函数不能使用非静态变量aaa
}
int aaa;
};
int person::a = 0;
void test() {
person::getA();
}
int main() {
test();
return 0;
}
4.9c++ 对象模型和this指针
- 空对象占用的内存为1字节
- 只有非静态成员变量属于类的对象上
class a{
}//空类大小为1字节
class a{
int a;
}//此时类大小为4字节
4.10 this 指针
- this指针指向被调用的成员函数所属对象
- 类的非静态成员函数中返回对象本身 *return this
#include<iostream>
#include <string>
using namespace std;
class person
{
public:
person(int name) {
this->name = name;
}
void show() {
cout << this->name << endl;
}
person& addPerson(person p2) {
this->name += p2.name;
return *this;//可以使用链式编程 返回函数自身
}
int name;
};
void test() {
person p(33);
person P2(33);
p.addPerson(33).addPerson(33);//链式编程
p.show();
}
int main() {
test();
return 0;
}
4.11空指针调用成员函数
#include<iostream>
#include <string>
using namespace std;
class person
{
public:
person(int name) {
this->name = name;
}
void show() {
if (this == NULL)//增加程序的健壮性
{
cout << "空指针访问" << endl;
return;
}
cout << this->name << endl;
}
int name;
};
int main() {
person* p = NULL;//空指针
p->show();
return 0;
}
4.12 const 修饰成员函数
常函数
-
成员函数后加const称之为常函数 -
常函数内部不可以修饰成员属性 -
成员属性添加mutable关键字后可以修改
常对象
-
声明对象之前添加const称之为常对象 -
常对象只能调用常函数
#include<iostream>
#include <string>
using namespace std;
class person
{
public:
person(int name, int money) {
this->money = money;
this->name = name;
}
void show() const { //常函数
//this->name = 100;
this->money = 100;
cout << this->name << endl;
}
void fun() {};
int name;//常函数不能修改
mutable int money;//常函数可以修改
};
void test() {
}
int main() {
const person p(12, 13);
p.show();
//p.fun();//常对象只能调用常函数
return 0;
}
4.13友元 friend
-
全局函数做友元 #include<iostream>
#include <string>
using namespace std;
// 全局函数友元
class Building
{
friend void goodGayvisit(Building* p);//全局函数的友元
public:
Building();
~Building();
string sittingRoom;
private:
string bedRoom;
};
Building::Building()
{
this->sittingRoom = "客厅";
this->bedRoom = "卧室";
}
void goodGayvisit(Building* p) {//全局函数访问
cout << "好基友访问客厅" << p->sittingRoom << endl;
cout << "好基友访问卧室" << p->bedRoom << endl;
}
Building::~Building()
{
cout << "Building 的析构函数" << endl;
}
void test() {
Building p;
goodGayvisit(&p);//函数参数需要指针,在此传递地址
}
int main() {
test();
return 0;
}
-
类做友元 #include<iostream>
#include <string>
using namespace std;
// 类友元
class BUiling
{
friend class gay;//友元类
public:
BUiling();
~BUiling();
string sitting;
private:
string bed;
};
BUiling::BUiling()
{
this->bed = "卧室";
this->sitting = "客厅";
}
BUiling::~BUiling()
{
}
class gay
{
public:
gay();
~gay();
BUiling* bu;
void visit(BUiling* bu);
private:
};
gay::gay()
{
bu = new BUiling;
}
void gay::visit(BUiling* p) {
cout << p->sitting << endl;
cout << p->bed << endl;//私有属性不可访问 需添加友元
}
gay::~gay()
{
}
void test() {
gay gg;
BUiling p;
gg.visit(&p);
}
int main() {
test();
return 0;
}
-
成员函数做友元
与上述函数一致 加上友即可
4.14 运算符重载四则运算
可以进行函数重载
#include<iostream>
#include <string>
using namespace std;
// 运算符重载
class Person
{
friend Person operator-(Person& a, Person& b);
public:
Person();
~Person();
public:
int a;
int b;
};
Person::Person()
{
a = 3;
b = 3;
cout << "程序构造初始化" << endl;
}
Person operator-(Person& a, Person& b) {
Person tem;
tem.a = a.a + b.a;
tem.b = a.b + b.b;
return tem;
}
Person::~Person()
{
cout << "程序析构退出" << endl;
}
void test() {
Person p1;
p1.a = 1;
p1.b = 2;
Person p2;
p2.a = 3;
p2.b = 4;
Person p3 = p1 - p2;
cout << "a=" << p3.a << "b=" << p3.b << endl;
}
int main() {
test();
return 0;
}
4.15 左移运算符重载
就是重新定义输出 cout 使之可以直接对对象进行输出
4.16 递增运算符重载
<< ++ – == !=
int a = 10;
cout << a++ << endl;// a=10
cout<<a<<endl;//a=10
int b=10;
cout<<++b<<endl;//b=11
cout<<b<,endl;/b=11
int *a;表示将数据防在堆区 需要使用 new int (a)进行申请初始化 需要在析构函数中进行 delete a 进行释放
// 返回自身 使用*this 则函数使用 A&进行返回引用,才是返回真正的自身 不加& 就是返回值 拷贝一份一样的数据
1前置递增(++a)返回的是引用
后置递增(a++)返回的是值
仿函数 ()的重载
4.17 匿名函数
#include<iostream>
#include <string>
using namespace std;
// 匿名对象
class MyAdd
{
public:
int operator()(int a, int b) {
return a + b;
}
MyAdd();
~MyAdd();
};
int main() {
MyAdd m;
cout << m(1, 2) << endl;
cout << MyAdd()(22, 22) << endl;// 匿名函数
return 0;
}
5.继承
- 继承的基本语法 class 子类 :继承方式 父类
#include<iostream>
#include <string>
using namespace std;
// 继承
class Base
{
public:
Base();
~Base();
void head() {
cout << "head" << endl;
}
void body() {
cout << "body" << endl;
}
void bottom() {
cout << "bottom" << endl;
}
private:
};
class son : public Base {//son 继承 Base类
public:
void getson() {
cout << "子类的方法" << endl;
}
};
int main() {
son s;
s.head();
s.body();
s.bottom();
s.getson();
return 0;
}
5.1 继承中的对象模型
父类中所有非静态产成员属性都会被子类继承下去
5.2继承中的构造和析构顺序
现有父类构造 --子构造-子析构-父析构
5.2继承中同名成员处理
同名成员–需要添加父类的作用域 Father:: a 否则直接调用就是调用的子类 子类会直接隐藏和父类同名成员函数
5.3继承静态成员函数的处理方式
访问子类中的直接访问
访问父类的需要添加作用域
通过类方式访问 son::Base::method 第一个::表示使用类的方式访问 第二个表示Base的作用域
5.4多继承语法
class 子类: 继承方式 父类1 ,继承方式 父类2
当父类出现成名成员 需要添加作用域
一般不建议使用
5.5 菱形继承
使用虚继承解决数据不一致的问题 virtual 解决子类继承两份相同的数据,导致资源浪费毫无意义
6.多态
6.1多态的基本概念
#include<iostream>
#include <string>
using namespace std;
// 继承 运行地址晚绑定
class Animal {
public:
virtual void speak() {// 不加 virtual 关键字 第24就是打印”动物在说话“
cout << "动物在说话" << endl;
}
};
class Cat :public Animal {
public:
void speak() {
cout << "小猫在说话" << endl;
}
};
void dospeak(Animal& animal) {//父类指针或者引用指向子类对象
animal.speak();
}
int main() {
Cat cat;
dospeak(cat);
return 0;
}
6.2 纯虚函数
当类中有了纯虚函数, 这类成为抽象类
#include<iostream>
#include <string>
using namespace std;
// 继承
class Animal {
public:
virtual void speak() = 0;// 纯虚函数 不能够实例化对象 子类必须进行函数重写
};
class Cat :public Animal {
public:
void speak() {
cout << "小猫在说话" << endl;
}
};
int main() {
Animal* c = new Cat;// 指针的方式可以使得new 其他子类 多态的一种体现
c->speak();
delete c;// 堆区使用之后必须进行释放
return 0;
}
6.3虚析构和纯虚析构
虚析构 可以解决父类指针释放子类对象不干净的问题 ---- 子类有数据开辟到堆区
(* a作为函数参数 可以使用 new A—a是A的实现类)
7文件操作
7.1写文件
#include<iostream>
#include <string>
#include<fstream>
using namespace std;
// 文件的读写
int main() {
ofstream ofs;
ofs.open("exam.txt", ios::out);
ofs << "测试" << endl;
ofs.close();// 文件写之后需要进行文件关闭
return 0;
}
7.2读文件
#include<iostream>
#include <string>
#include<fstream>
using namespace std;
// 文件读
int main() {
ifstream ifs;
ifs.open("exam.txt", ios::in);
if (!ifs.is_open()) {
cout << "打开文件失败" << endl;
return 0;
}
string buffer;
while (getline(ifs, buffer))
{
cout << buffer << endl;
}
ifs.close();
return 0;
}
8、c++提高编程
8.1函数模板
#include<iostream>
#include <string>
#include<fstream>
using namespace std;
// 函数模板
template <typename T>
void mswap(T& a, T& b) {
int tem;
tem = a;
a = b;
b = tem;
}
int main() {
int a = 1;
int b = 2;
// 自动推导确定数据类型
mswap(a, b);
// 显示指定其数据类型
mswap<int>(a, b);
cout << a << endl;
return 0;
}
8.2函数模板注意事项
- 自动类型推导,必须推导出一致的数据类型T,才可以使用
- 模板必须要确定T的数据类型,才可以使用
8.3普通函数和函数模板的区别
- 普通函数调用可以发生 *隐式类型转换=====add(10,‘c’)可以将‘c’转换为ASCII编码 99*
- 函数模板 用自动类型推导,不可以发生隐式类型转换
- 函数模板 用显式指定类型,可以发生隐式类型转换
- 函数模板也可以发生重载
8.4类模板和函数模板的区别
- 类模板没有自动类型推到的使用方式
- 类模板在模板参数列表中可以有默认参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RvUuvypT-1637113785266)(C:\Users\15281\AppData\Roaming\Typora\typora-user-images\image-20211103165414699.png)]
8.5 类模板中成员函数的创建时机
- 普通类中的成员函数一开始就可以创建
- 类模板的成员函数在调用时才创建
9.STL使用
分为 容器、算法和迭代器
|