C++程序在执行时,内存大致分为四个区域
在程序运行前,生成了exe可执行程序,未执行该程序前分为两个区域
代码区:
全局区:
#include <iostream>
#include <string>
using namespace std;
// 定义全局变量
int global_a = 1;
int global_b = 1;
// const修饰全局变量
int const_global_a = 1;
int const_global_b = 1;
void main() {
// 局部变量
int a = 1;
int b = 1;
cout << "局部变量a的地址" << (int)&a << endl;
cout << "局部变量b的地址" << (int)&b << endl;
//全局变量
cout << "全局变量global_a的地址" << (int)&global_a << endl;
cout << "全局变量global_a的地址" << (int)&global_b << endl;
// 静态变量
static int static_a = 1;
static int static_b = 1;
cout << "静态变量static_a的地址" << (int)&static_a << endl;
cout << "静态变量static_b的地址" << (int)&static_b << endl;
// 常量
// 字符串常量
cout << "字符串常量的地址" << (int)&"hello world" << endl;
// const全局变量
cout << "const全局变量const_global_a的地址" << (int)&const_global_a << endl;
cout << "const全局变量const_global_b的地址" << (int)&const_global_b << endl;
// const局部变量
const int const_a = 1;
const int const_b = 1;
cout << "const局部变量const_a的地址" << (int)&const_a << endl;
cout << "const局部变量const_b的地址" << (int)&const_b << endl;
}
栈区:
选择X86运行
#include <iostream>
#include <string>
using namespace std;
// 形参和局部变量存放在栈区
int* func(int b) {
// 用指针接收局部变量a的地址并返回
int a = 10;
return &a;
}
void main() {
int *p = func(10);
cout << *p << endl;
cout << *p << endl;
}
堆区:
#include <iostream>
#include <string>
using namespace std;
/*
* 数据10是存放在堆区的,但是指针是局部变量任然是存放在栈区
*/
int* func1() {
int *p = new int(10);
return p;
}
void main() {
int* p = func1();
cout << *p << endl;
cout << (int)p << endl;
return;
}
c++使用new在堆区开辟内存空间,需要使用delete指令手动释放
#include <iostream>
#include <string>
using namespace std;
/*
* 数据10是存放在堆区的,但是指针是局部变量任然是存放在栈区
*/
int* func2() {
int* p = new int(10);
return p;
}
void test01() {
int* p = func2();
cout << *p << endl;
// delete释放堆区内存
delete p;
// cout << *p << endl;
}
void test02() {
int* ptr = new int[3];
for (int i = 0; i < 3; i++) {
ptr[i] = i + 100;
}
for (int i = 0; i < 3; i++) {
cout << ptr[i] << endl;
}
// delete[]释放堆区数组内存
delete[] ptr;
}
void main() {
test01();
test02();
return;
}
**作用:**给变量起别名
语法:数据类型 &别名 = 原名
#include <iostream>
using namespace std;
void main() {
int a = 1;
int& b = a;
cout << a << endl;
cout << b << endl;
b = 100;
cout << a << endl;
cout << b << endl;
}
#include <iostream>
using namespace std;
void main() {
int a = 1;
int& b = a;
cout << a << endl;
cout << b << endl;
b = 100;
cout << a << endl;
cout << b << endl;
// 不做初始化会报错
//int& c;
int c = 2;
// 引用无法更改
//&b = c;
// 这是赋值操作,没有改变引用
b = c;
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
**作用:**函数传参时,使用引用让形参修饰实参
**优点:**简化指针修改实参
#include <iostream>
using namespace std;
// 值传递
void swap01(int a, int b) {
int temp = a;
a = b;
b = temp;
cout << "swap01 a=" << a << endl;
cout << "swap01 b=" << b << endl;
}
// 地址传递
void swap02(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
cout << "swap02 a=" << *a << endl;
cout << "swap02 b=" << *b << endl;
}
// 引用传递
void swap03(int &a, int &b) {
int temp = a;
a = b;
b = temp;
cout << "swap03 a=" << a << endl;
cout << "swap03 b=" << b << endl;
}
void main() {
int a = 1, b = 2;
// swap01(a, b);
// swap02(&a, &b);
swap03(a, b);
cout << a << endl;
cout << b << endl;
}
**作用:**作为函数返回值返回
#include <iostream>
using namespace std;
int& test01() {
int a = 10;
return a;
}
int& test02() {
static int a = 10;
return a;
}
void main() {
//int& ref = test01();
int& ref = test02();
cout << ref << endl;
// 如果函数的返回值是引用,这个函数可以作为左值
test02() = 1000;
cout << ref << endl;
}
引用的本质在C++内部实现是一个指针常量,引用不可修改,值可以修改
#include <iostream>
using namespace std;
void func(int& ref) {
ref = 100;
}
void main() {
int a = 10;
// 等价 int* const ref = &a;
int& ref = a;
// 等价*ref = 20;
ref = 20;
cout << a << endl;
cout << ref << endl;
func(a);
cout << a << endl;
cout << ref << endl;
}
作用:在函数形参列表中,用const来修饰形参引用,防止误操作
#include <iostream>
using namespace std;
void showInt(const int& ref) {
// ref = 100;加入const变为只读不可修改
cout << ref << endl;
}
void main() {
int a = 10;
showInt(a);
//showInt(10);如果showInt的形参没有用const修饰,这是不允许的操作,非常量引用的初始值必须为左值
}
函数的形参是可以与默认值的
#include <iostream>
using namespace std;
void func1(int a, int b, int c) {
int sum = a + b + c;
cout << "func1求和=" << sum << endl;
}
// 如果自己传递了参数就用参数,没传递就用默认值
void func2(int a, int b=2, int c=2) {
int sum = a + b + c;
cout << "func2求和=" << sum << endl;
}
/*
*
* 如果函数某个参数有默认值,则它后面的参数都要有默认值
* 这个函数定义是不被允许的,运行会提示形参2和3缺少默认实参
* void func3(int a=1, int b, int c) {
* int sum = a + b + c;
* cout << "func3求和=" << sum << endl;
* }
*
*/
/*
*
* 函数声明和实现中,只允许在声明中设置形参默认值,或者在实现中设置默认值
* 否则运行会提示参数重定义
* void func4(int a=10, int b=10);
* void func4(int a=11, int b=1) {
* }
*
*/
void main() {
func1(1, 2, 3);//6
func2(1, 3, 3);//7
func2(1);//5
}
函数的参数列表里可以有占位参数,调用函数时必须填补该位置,占位参数也可以有默认值
语法 void func(数据类型){}
#include <iostream>
using namespace std;
// 第二个参数是占位参数
void func1(int a, int) {
cout << "func1" << endl;
}
// 占位参数也可以有默认参数
void func2(int a, int=10) {
cout << "func2" << endl;
}
void main() {
func1(1, 1);
func2(1);
}
作用:函数名相同,提高复用性
满足函数重载的条件如下:
注意:函数返回值类型不能作为函数重载的条件
#include <iostream>
using namespace std;
// 函数重载
void func(int a,float b) {
cout << "func1(int a,float b)" << endl;
}
// 参数顺序不同
void func(float b, int a) {
cout << "func1(float b, int a) 参数顺序不同" << endl;
}
// 参数类型不同
void func(int a, int b) {
cout << "func1(float a, float b) 参数类型不同" << endl;
}
// 参数个数不同
void func() {
cout << "func1() 参数个数不同" << endl;
}
// 返回值类型不能作为函数重载的条件
// int func() {
// cout << "func1() 参数个数不同" << endl;
// }
void main() {
func(1, (float)1);
func((float)1, 1);
func(1, 1);
func();
}
注意事项:
#include <iostream>
using namespace std;
// 函数重载与参数引用
void myFunc(int& a) {
cout << "myFunc(int &a)调用" << endl;
}
void myFunc(const int &a) {
cout << "myFunc(const int &a)调用" << endl;
}
// 函数重载与参数默认值会报错有二义性
void myFunc1(int a) {
cout << "myFunc1(int a)" << endl;
}
void myFunc1(int a=1) {
cout << "myFunc1(int a)" << endl;
}
void main() {
int a = 1;
myFunc(a);
myFunc(10);
//myFunc1(10);
}
C++面向对象的三大特性:封装、继承、多态
创建语法
#include <iostream>
using namespace std;
const double PI = 3.14;
// class关键字声明一个类
class Circle
{
public:
// 属性
int m_r;
// 行为
double calculateZC() {
return 2 * PI * m_r;
}
};
void main() {
Circle c1;
c1.m_r = 100;
cout << c1.calculateZC() <<endl;
}
将属性和行为放在不同的权限下方便管理,共有三个权限
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string name;
void func() {
name = "WenYL";
car = "benz";
password = 123;
}
private:
int password;
protected:
string car;
};
void main() {
Person p1;
cout << p1.name << endl;
// protected权限类外无法访问
// cout << p1.car << endl;
// private权限类外无法访问
// cout << p1.password << endl;
}
struct和class唯一的区别就在于默认的访问权限不同
#include <iostream>
#include <string>
using namespace std;
class Man
{
string username;
};
struct Woman {
string username;
};
void main() {
Man p1;
// 类属性默认私有无法访问
// cout << p1.username << endl;
Woman p2;
cout << p2.username << endl;
}
将成员属性设置为私有有以下优点:
#include <iostream>
#include <string>
using namespace std;
class Man
{
private:
string username;
int age;
string idol;
public:
// username可读可写
string getUsername() {
return username;
}
void setUsername(string name) {
username = name;
}
// age可读,设置年龄时控制年龄范围
int getAge() {
return age;
}
void setAge(int newAge) {
if (newAge < 0 || newAge > 150) {
cout << "年龄输入有误" << endl;
return;
}
age = newAge;
}
// ido只写
void setIdo(string newIdol) {
idol = newIdol;
}
};
void main() {
Man p1;
p1.setUsername("龙");
cout << p1.getUsername() << endl;
p1.setAge(150);
cout << p1.getAge() << endl;
p1.setIdo("坤坤");
}
对象的初始化和清理也是两个非常重要的安全问题,C++提供构造函数和析构函数来解决这两个问题,这两个函数会被编译器自动调用,完成对象的初始化和清理工作,如果在编写类时未提供这两个函数,编译器会默认给我们提供这两个函数,但是编译器提供的默认构造函数和析构函数是空实现
构造函数语法:类名(){}
析构函数语法:~类名(){}
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person() {
cout << "无参构造函数" << endl;
}
Person(string name) {
username = name;
}
~Person() {
cout << "析构函数" << endl;
}
private:
// 属性
string username;
};
void test01(){
Person p1;
}
int main() {
test01();
system("pause");
return 0;
}
构造函数有两种分类方式:
三种调用方式:
注意:
#include <iostream>
#include <string>
using namespace std;
class Cat
{
public:
Cat() {
cout << "无参构造函数" << endl;
}
Cat(const Cat&p) {
cout << "拷贝构造函数" << endl;
age = p.age;
}
Cat(int newAge) {
cout << "有参构造函数" << endl;
age = newAge;
}
~Cat() {
cout << "析构函数" << endl;
}
// 属性
int age;
};
// 括号法调用
void test1() {
Cat p1;//无参
// 调用默认构造函数时,不要加().编译器会默认为一个函数的声明
// Cat p11();
Cat p2(12);//有参
Cat p3(p2);//拷贝
}
// 显示调用
void test2() {
Cat p1;
Cat p2 = Cat(10);
Cat p3 = Cat(p2);
// Cat(10) 是一个匿名对象,当前行执行结束,系统会立即回收匿名对象
cout << "匿名对象" << endl;
Cat(10);
// 不要利用拷贝构造函数初始化匿名对象,编译器会认为 Cat (p3) = Cat(p3);
// Cat(p3);
}
// 隐式转换
void test3() {
Cat p1;
Cat p2 = 10;//相当于写了Cat p1 = Cat(10);
Cat p3 = p2;
}
int main() {
// test1();
// test2();
test3();
system("pause");
return 0;
}
拷贝嗲用函数调用时机有三种情况
#include <iostream>
#include <string>
using namespace std;
class Dog
{
public:
Dog() {
cout << "无参构造函数" << endl;
}
Dog(string name) {
username = name;
cout << "有参构造函数" << endl;
}
Dog(const Dog &dog) {
cout << "拷贝函数" << endl;
username = dog.username;
}
~Dog() {
cout << "析构函数" << endl;
}
private:
// 属性
string username;
};
// 使用以创建的实例构造新的实例
void test031() {
Dog p1("花花");
Dog p2 = Dog(p1);
}
// 值传递的方式给函数参数传值
void setName(Dog dog) {
}
void test032() {
Dog p1;
setName(p1);
}
// 值方式返回局部对象
Dog returnDog() {
Dog p1;
cout << (int) & p1 << endl;
return p1;
}
void test033() {
Dog p1 = returnDog();
cout << (int)&p1 << endl;
}
int main() {
//test031();
//test032();
test033();
system("pause");
return 0;
}
默认情况下,c++编译器至少给一个类添加三个构造函数
拷贝函数调用规则如下:
#include <iostream>
using namespace std;
class Huawei
{
public:
Huawei() {
cout << "无参构造函数" << endl;
};
Huawei(int m) {
cout << "有无参构造函数" << endl;
money = m;
};
Huawei(const Huawei &huawei) {
cout << "拷贝构造函数" << endl;
};
~Huawei() {
cout << "析构函数" << endl;
};
int money;
};
void test041() {
// 提供了有参构造时,将无参构造注释,这里就会调用失败,但是拷贝构造函数不影响
Huawei h;
h.money = 100;
// 将Huawei的拷贝构造函数注释后任然可以调用成功,但是不在输出 拷贝构造函数
// 将Huawei的有参无参构造函数注释后,在调用Huawei h;Huawei h(100);就会报错,因为此时编译器不在提供默认的有参和无参构造函数
Huawei h1 = Huawei(h);
}
int main() {
test041();
system("pause");
return 0;
}
如果类有属性在堆区,则需要自己实现深拷贝和析构函数释放内存
#include <iostream>
using namespace std;
class Xiaomi
{
public:
Xiaomi() {
cout << "无参构造函数" << endl;
};
Xiaomi(int m, double w) {
cout << "有无参构造函数" << endl;
money = m;
weight = new double(w);
};
Xiaomi(const Xiaomi& xiaomi) {
cout << "拷贝构造函数" << endl;
money = xiaomi.money;
// 根据传入的对象的引用值重新申请一块内存来存储
weight = new double(*xiaomi.weight);
};
~Xiaomi() {
if (weight != NULL) {
delete weight;
weight = NULL;
}
cout << "析构函数" << endl;
};
int money;
double *weight;
};
void test051() {
Xiaomi x1(10,0.01);
// 这里默认进行的是浅拷贝,两个xiaomi对象的weight属性指向同一个weigh
// 当x2执行完析构函数时,它指向的堆内存已经被释放,x1调用析构函数要去释放内存就报错了
Xiaomi x2(x1);
}
int main() {
test051();
system("pause");
return 0;
}
c++类初始化成员列表语法如下
构造函数():属性1(值1),属性2(值2)......
#include <iostream>
using namespace std;
class Iphone {
public:
int money;
int memory;
Iphone(int m1, int m2):money(m1), memory(m2){
}
};
void test061() {
Iphone i(1, 1);
cout << i.money << endl;
cout << i.memory << endl;
}
int main() {
test061();
system("pause");
return 0;
}
构造是先调用对象成员的构造,在调用自己的构造,析构时先调用自己的析构,在调用对象成员的析构
#include <iostream>
#include <string>
using namespace std;
class Vivo {
public:
Vivo() {
cout << "Vivo无参构造函数" << endl;
}
Vivo(string n):name(n) {
cout << "Vivo有参构造函数" << endl;
}
~Vivo() {
cout << "Vivo析构函数" << endl;
}
private:
string name;
};
class Student {
public:
Student(string n,string vivoName): name(n), vivo(vivoName){
cout << "Student有参构造函数" << endl;
}
~Student() {
cout << "Student有参构造函数" << endl;
}
private:
string name;
Vivo vivo;
};
void test071() {
Student i("龙", "P60");
}
int main() {
test071();
system("pause");
return 0;
}
静态成员分为:
静态成员变量案例:
#include <iostream>
#include <string>
using namespace std;
class Water {
public:
// 所有对象共享一份数据,编译阶段就分配内存
static string color;
private:
static int weight;
};
int Water::weight = 1;
string Water::color = "blue";
int main() {
Water w;
cout << w.color << endl;
Water w1;
w1.color = "red";
// 通过对象进行访问
cout << w.color << endl;
// 通过类名进行访问
cout << Water::color << endl;
// 静态成员变量也有权限控制
// cout << Water::weight << endl;
system("pause");
return 0;
}
静态成员函数案例:
#include <iostream>
#include <string>
using namespace std;
class Stone {
public:
static int weight;
string color;
static void changeData() {
// 静态成员方法可以访问静态成员变量
weight = 10;
/*
* 静态成员方法不能访问非静态成员变量
* 因为静态方法和静态变量是所有对象共享的,但是每个对象持有自己的非静态成员变量
* 在静态方法中操作非静态成员变量,编译器就无法判断需要操作的实际是哪个对象的
* 成员变量
*/
//color = "red";
}
static void staticFunc() {
cout << "调用staticFunc()" << endl;
}
private:
static void staticFunc2() {
cout << "调用staticFunc2()" << endl;
}
};
int Stone::weight = 1;
int main020802() {
Stone w;
// 引用调用
w.staticFunc();
// 类名调用
Stone::staticFunc();
//类外无法访问私有静态成员函数
//Stone::staticFunc2();
system("pause");
return 0;
}
在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上
#include <iostream>
using namespace std;
class Window {
int w_a;
static int size;
void getSize() {}
static void getSize1() {}
};
void test030101() {
Window w;
// c++编译器会给每个空对象也分配一个内存空间,是为了区分对象占内存的位置
// 每个空对象也会有一个独一无二的地址
cout << sizeof(w) << endl;
}
/*
* 在Window中添加属性int w_a;
* 结果为4,说明成员属性在类上
*/
void test030102() {
Window w;
cout << sizeof(w) << endl;
}
/*
* 在Window中添加静态属性static int size;
* 结果依然为4,说明静态成员属性不在类上
*/
void test030103() {
Window w;
cout << sizeof(w) << endl;
}
/*
* 在Window中添加方法
* void getSize() {}
* 结果依然为4,说明静态成员属性不在类上
*/
void test030104() {
Window w;
cout << sizeof(w) << endl;
}
/*
* 在Window中添加方法
* static void getSize1() {}
* 结果依然为4,说明静态成员属性不在类上
*/
void test030105() {
Window w;
cout << sizeof(w) << endl;
}
int main() {
//test030101();
//test030102();
//test030103();
//test030104();
test030105();
system("pause");
return 0;
}
每一个非静态成员函数只会诞生一份函数示例,也就是说多个同类型的对象会公用一块代码
this指针指向被调用的成员函数所属的对象
this指针的用途:
#include <iostream>
using namespace std;
class Wind {
public:
Wind(int level) {
this->level = level;
}
int getLevel() {
return this->level;
}
Wind& addLevel(const Wind& wind) {
this->level += wind.level;
return *this;
}
private:
int level;
};
void test030201() {
Wind w(5);
cout << w.getLevel() << endl;
}
void test030202() {
Wind w1(5);
Wind w2(5);
w1.addLevel(w2).addLevel(w2);
cout << w1.getLevel() << endl;
}
int main() {
// test030201();
test030202();
system("pause");
return 0;
}
空指针调用成员函数
#include <iostream>
using namespace std;
class Bei {
public:
void showClassname() {
cout << "showClassname()" << endl;
}
void showNum() {
// 报错是因为传入的this为空,添加下面判断后即可
if (this == NULL) {
return;
}
cout << num << endl;
}
int num;
};
void test030301() {
Bei* p = NULL;
p->showClassname();
// 添加了下面代码就报错
p->showNum();
}
int main() {
test030301();
system("pause");
return 0;
}
常含数:
常对象:
#include <iostream>
using namespace std;
// 常函数
class A {
public:
int m_a;
mutable int m_b;
void showMA() {
m_a = 10;
cout << m_a<<endl;
}
/*
* this指针是一个指针常量,不能修改他的指向const A* this;
* 因此在函数内调用this = NULL;会报错的
* 若我们在函数实现这里在加一个const,即void showMA() const{}
* 则此时指针就成了const A* const this;即不能修改值,也不能修改this指向
*/
void showMA1() const {
//m_a = 50;
}
void showMB() const {
m_b = 20;
}
};
void test030401() {
A a;
a.showMA();
a.showMA1();
a.showMB();
}
// 常对象
void test030402() {
const A a;
// 常对象无法修改普通成员属性
// a.m_a = 10;
a.m_b = 50;
// 常对象只能调用常函数,因为普通成员函数可以修改属性
// a.showMA();
a.showMA1();
}
int main() {
test030401();
test030402();
system("pause");
return 0;
}
**作用:**让一个函数或者类访问另一个类中的私有成员
友元的三种实现:
#include <iostream>
#include <string>
using namespace std;
class Building {
// goodFriend函数是Building友元,goodFriend可以访问Building的私有属性
friend void goodFriend(Building* building);
public:
string m_SittingRoom;
private:
string m_BedRoom;
public:
Building() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
};
// 全局函数
void goodFriend(Building* building) {
cout << "好朋友在访问" << building->m_SittingRoom << endl;
cout << "好朋友在访问" << building->m_BedRoom << endl;
}
void test040101() {
Building building;
goodFriend(&building);
}
int main() {
test040101();
return 0;
}
#include <iostream>
#include <string>
using namespace std;
class Home {
friend class GoodFriend;
public:
string m_SittingRoom;
private:
string m_BedRoom;
public:
Home();
};
Home::Home() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
class GoodFriend {
public:
GoodFriend();
Home* m_Home;
void visit();
};
GoodFriend::GoodFriend() {
m_Home = new Home;
}
void GoodFriend::visit() {
cout << "好朋友在访问" << m_Home->m_SittingRoom << endl;
cout << "好朋友在访问" << m_Home->m_BedRoom << endl;
}
void test040201() {
GoodFriend g;
g.visit();
}
int main() {
test040201();
return 0;
}
#include <iostream>
#include <string>
using namespace std;
class House;
class GoodGay {
public:
House* house;
GoodGay();
void visit1();//可以访问私有成员
void visit2();//不可访问私有成员
};
class House {
friend void GoodGay::visit1();
public:
string m_SittingRoom;
House();
private:
string m_BedRoom;
};
House::House() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay() {
house = new House;
}
void GoodGay::visit1() {
cout << "visit1正在访问" << house->m_SittingRoom<<endl;
cout << "visit1正在访问" << house->m_BedRoom << endl;
}
void GoodGay::visit2() {
cout << "visit2正在访问" << house->m_SittingRoom << endl;
// 下面代码不能访问
//cout << "visit2正在访问" << house->m_BedRoom << endl;
}
void test040301() {
GoodGay goodGay;
goodGay.visit1();
}
int main() {
test040301();
return 0;
}
对已有的运算符重定义,以适应不同的数据结构
**作用:**实现两个自定义数据类型相加的运算
注意:
#include <iostream>
#include <string>
using namespace std;
class Human {
public:
int m_A;
int m_B;
Human() {}
Human(int a,int b): m_A(a), m_B(b){}
/*
* 成员函数重载+号
* Human h3 = h1 + h2;
* 本质是p1.operator+(p2);
*/
//Human operator+(Human&p) {
//Human temp;
//temp.m_A = this->m_A + p.m_A;
//temp.m_B = this->m_B + p.m_B;
//return temp;
//}
};
/*
* 全局函数重载+号
* Human h3 = h1 + h2;
* 本质是p1.operator+(p1,p2);
*/
Human operator+(Human& p1, Human& p2) {
Human temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
/*
* 运算符重载也可以发生函数重载
*/
Human operator+(Human & p1, int num) {
Human temp;
temp.m_A = p1.m_A + num;
temp.m_B = p1.m_B + num;
return temp;
}
void test050101() {
Human h1(1, 1);
Human h2(2, 2);
Human h3 = h1 + h2;
cout << h3.m_A << endl;
cout << h3.m_B << endl;
Human h4 = h1 + 10;
cout << h4.m_A << endl;
cout << h4.m_B << endl;
}
int main() {
test050101();
return 0;
}
#include <iostream>
using namespace std;
class Person {
public:
int m_A;
int m_B;
Person() {}
Person(int a, int b) : m_A(a), m_B(b) {}
};
ostream& operator<<(ostream& cout, Person& p) {
cout << p.m_A << " " << p.m_B;
return cout;
}
void test052101() {
Person p(1,1);
cout << p <<endl;
}
int main() {
test052101();
return 0;
}
#include <iostream>
using namespace std;
class MyInteger {
friend ostream& operator<<(ostream& cout, const MyInteger& p);
public:
void setNum(int num) {
this->num = num;
}
// 重载前置++运算符
MyInteger& operator++() {
num++;
return *this;
}
// 重载后置++运算符,这里int用于区分前置后置
MyInteger operator++(int) {
// temp在函数执行结束就被释放,因此这里返回值,而不是返回引用
MyInteger temp = *this;
num++;
return temp;
}
private:
int num;
};
//重载<<
ostream& operator<<(ostream& cout, const MyInteger& p) {
cout << p.num;
return cout;
}
void test050301() {
MyInteger i;
i.setNum(0);
cout << ++i << endl;
}
void test050302() {
MyInteger myInt;
myInt.setNum(3);
cout << myInt++ << endl;
cout << myInt << endl;
}
int main() {
test050301();
test050302();
return 0;
}
c++默认给一个类添加了至少四个函数
如果类中有属性指向堆区,赋值操作时,也会出现深浅拷贝问题
#include <iostream>
using namespace std;
class PersonA {
public:
PersonA() {}
PersonA(int age) {
// 在堆区开辟一块内存,并使age指针指向它
this->age = new int(age);
}
~PersonA() {
if (this->age != NULL) {
delete this->age;
this->age = NULL;
}
cout << "析构函数" << endl;
}
PersonA& operator=(PersonA& p) {
// 深拷贝,先释放自己的age属性占据的堆内存空间,然后重新申请
if (this->age != NULL) {
delete this->age;
this->age = NULL;
}
this->age = new int(*p.age);
return *this;
}
// 创建一个int指针
int *age;
};
void test050401() {
PersonA p1(18);
PersonA p2(20);
PersonA p3(30);
p3 = p2 = p1;
// 复制后,进行的时浅拷贝,直接将p1的age属性存储的堆内存地址赋值给了p2
cout << "address=" << p1.age << " value=" << *p1.age << endl;
cout << "address=" << p2.age << " value=" << *p2.age << endl;
cout << "address=" << p3.age << " value=" << *p3.age << endl;
}
int main() {
test050401();
return 0;
}
**作用:**自定义两个自定义数据类型的比较
#include <iostream>
using namespace std;
class Person0505 {
public:
Person0505() {}
Person0505(int age) {
// 在堆区开辟一块内存,并使age指针指向它
this->age = new int(age);
}
~Person0505() {
if (this->age != NULL) {
delete this->age;
this->age = NULL;
}
cout << "析构函数" << endl;
}
bool operator==(Person0505& p) {
return *this->age == *p.age;
}
// 创建一个int指针
int* age;
};
void test050501() {
Person0505 p1(18);
Person0505 p2(20);
Person0505 p3(20);
cout << (p1 == p2) << endl;
cout << (p3 == p2) << endl;
}
int main() {
test050501();
return 0;
}
#include <iostream>
#include <string>
using namespace std;
class MyPrinter {
public:
void operator()(string text) {
cout << text;
}
};
void test050601() {
MyPrinter myPrinter;
myPrinter("hello world");
}
int main() {
test050601();
return 0;
}
继承是面向对象三大特性之一,使用继承关系可以减少冗余代码,提升代码复用,并优化代码结构设计
当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为 基类 ,新建的类称为 派生类 。
继承代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。
class derived-class: access-specifier base-class
其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。
#include <iostream>
#include <string>
using namespace std;
class BasePage {
public:
void header() {
cout << "首页" << endl;
}
void footer() {
cout << "帮助中心" << endl;
}
void left() {
cout << "Java" << endl;
}
};
class JavaPage :public BasePage {
public:
void content() {
cout << "java视频" << endl;
}
};
void test060101() {
JavaPage javaPage;
javaPage.header();
javaPage.content();
}
int main() {
test060101();
return 0;
}
当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:
#include <iostream>
#include <string>
using namespace std;
class Base1 {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 :public Base1 {
public:
void func() {
cout << m_A << endl;// 父类的public成员在子类依然是public
cout << m_B << endl;// 父类的protected成员在子类依然是protected
// cout << m_C << endl;// 父类的private成员在子类无法访问
}
};
class Son2 :protected Base1 {
public:
void func() {
cout << m_A << endl;// 父类的public成员在子类变成了protected
cout << m_B << endl;// 父类的protected成员在子类依然是protected
// cout << m_C << endl;// 父类的private成员在子类无法访问
}
};
class Son3 :private Base1 {
public:
void func() {
cout << m_A << endl;// 父类的public成员在子类变成了private
cout << m_B << endl;// 父类的protected成员在子类依然是private
// cout << m_C << endl;// 父类的private成员在子类无法访问
}
};
class GrandSon :public Son3 {
void func() {
//cout << m_A << endl;// 父类的private成员在子类无法访问
//cout << m_B << endl;// 父类的private成员在子类无法访问
// cout << m_C << endl;// 父类的private成员在子类无法访问
}
};
void test060201() {
cout << "类内访问" << endl;
Son1 son1;
son1.func();
cout << "类外访问" << endl;
cout << son1.m_A << endl;
// cout << son1.m_B << endl;// protected成员在类外无法访问
}
void test060202() {
cout << "类内访问" << endl;
Son2 son2;
son2.func();
cout << "类外访问" << endl;
// cout << son1.m_A << endl;// protected成员在类外无法访问
// cout << son1.m_B << endl;// protected成员在类外无法访问
}
void test060203() {
cout << "类内访问" << endl;
Son2 son2;
son2.func();
cout << "类外访问" << endl;
// cout << son1.m_A << endl;// protected成员在类外无法访问
// cout << son1.m_B << endl;// protected成员在类外无法访问
}
int main() {
test060201();
test060202();
test060203();
return 0;
}
通过VS 开发人员命令提示符,可以查看一个类的属性,操作步骤如下:
>cl /d1 reportSingleClassLayoutSon060301 "03 继承中的对象模型.cpp"
这里的Son060301是要查看的类,后面是它所在的cpp文件
#include <iostream>
#include <string>
using namespace std;
class Base060301 {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son060301 :public Base060301 {
public:
int m_D;
};
void test060301() {
Son060301 son1;
// 父类中所有非静态成员属性都会被子类继承
// 父类中私有成员属性被编译器隐藏了,因此访问不到,但是确实继承了
cout << sizeof(son1) << endl;
}
int main0603() {
test060301();
return 0;
}
构造函数:先调用父类,在调用子类
析构函数:先调用子类,再调用父类
#include <iostream>
#include <string>
using namespace std;
class Base060401 {
public:
Base060401() {
cout << "父类构造函数" << endl;
}
~Base060401() {
cout << "父类析构构函数" << endl;
}
};
class Son060401 :public Base060401 {
public:
Son060401() {
cout << "子类构造函数" << endl;
}
~Son060401() {
cout << "子类析构构函数" << endl;
}
};
void test060401() {
Son060401 son1;
}
int main() {
test060401();
return 0;
}
当子类和父类出现同名成员时
使用父类::同名属性即可访问
#include <iostream>
#include <string>
using namespace std;
class Base060501 {
public:
int m_A=50;
};
class Son060501 :public Base060501 {
public:
int m_A=100;
};
void test060501() {
Son060501 son1;
cout << son1.m_A << endl;
cout << son1.Base060501::m_A << endl;
}
int main() {
test060501();
return 0;
}
同名的静态成员处理方法与非静态成员处理方法一致
#include <iostream>
#include <string>
using namespace std;
class Base060601 {
public:
static int m_A;
static void func() {
cout << "父类方法" << endl;
}
static void func(int a) {
cout << "父类方法重载" << endl;
}
};
int Base060601::m_A = 50;
class Son060601 :public Base060601 {
public:
static int m_A;
static void func() {
cout << "子类方法" << endl;
}
};
int Son060601::m_A = 100;
void test060601() {
// 通过对象访问
Son060601 son1;
cout << "通过对象访问" << endl;
cout << son1.m_A << endl;
cout << son1.Base060601::m_A << endl;
cout << "通过类名访问" << endl;
cout << Base060601::m_A << endl;
cout << Son060601::Base060601::m_A << endl;
cout << Son060601::m_A << endl;
}
void test060602() {
// 通过对象访问
Son060601 son1;
cout << "通过对象访问" << endl;
son1.func();
son1.Base060601::func();
son1.Base060601::func(100);
cout << "通过类名访问" << endl;
Base060601::func();
Son060601::Base060601::func();
Son060601::func();
}
int main() {
test060601();
test060602();
return 0;
}
语法:
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};
#include <iostream>
#include <string>
using namespace std;
class Base060701 {
public:
int m_A = 100;
};
class Base060702 {
public:
int m_A = 150;
int m_B = 200;
};
class Son060701 :public Base060701,public Base060702 {
public:
int m_C = 300;
int m_D = 400;
};
void test060701() {
// 通过对象访问
Son060701 son1;
cout << "通过对象访问" << endl;
cout << son1.Base060701::m_A << endl;
cout << son1.Base060702::m_A << endl;
cout << son1.m_B << endl;
cout << son1.m_C << endl;
cout << son1.m_D << endl;
}
int main() {
test060701();
return 0;
}
一个父类被两个子类继承,这两个子类又被另一个子类继承,此时子类继承了两份数据
#include <iostream>
#include <string>
using namespace std;
class Animal {
public:
int m_age;
};
// 羊
class Sheep:public Animal {
public:
};
// 骆驼
class Camel :public Animal {
public:
};
// 羊驼
class Alpaca :public Camel, public Sheep {
public:
};
// 利用虚继承解决棱形继承的问题
class Sheep1 : virtual public Animal {
public:
};
// 骆驼
class Camel1 : virtual public Animal {
public:
};
// 羊驼
class Alpaca1 :public Camel1, public Sheep1 {
public:
};
void test060801() {
Alpaca a1;
//a1.m_age = 10;//此时无法直接访问
a1.Sheep::m_age = 10;
a1.Camel::m_age = 20;
cout << a1.Sheep::m_age << endl;
cout << a1.Camel::m_age << endl;
}
void test060802() {
Alpaca1 a1;
//a1.m_age = 10;//此时无法直接访问
a1.Sheep1::m_age = 10;
a1.Camel1::m_age = 20;
cout << a1.Sheep1::m_age << endl;
cout << a1.Camel1::m_age << endl;
}
int main() {
//test060801();
test060802();
return 0;
}
多态是c++三大特性之一
多态分为两类:
静态多态和动态多态的区分:
动态多态的满足条件:
#include <iostream>
#include <string>
using namespace std;
class Anima070101 {
public:
virtual void speak() {
cout << "动物在说话" << endl;
}
};
class Cat070101 :public Anima070101 {
public:
void speak() {
cout << "猫在说话" << endl;
}
};
class Dog070101 :public Anima070101 {
public:
void speak() {
cout << "狗在说话" << endl;
}
};
void test070701(Anima070101& animal) {
animal.speak();
}
int main() {
Cat070101 cat;
test070701(cat);
Dog070101 dog;
test070701(dog);
return 0;
}
纯虚函数语法:virtual 返回值类型 函数名(参数)=0;
抽象类:包含纯虚函数的类
抽象类的特点:
#include <iostream>
#include <string>
using namespace std;
class Anima070201 {
public:
virtual void speak() = 0;
};
class Cat070201 :public Anima070201 {
public:
void speak() {
cout << "猫在说话" << endl;
}
};
class Cat070202 :public Anima070201 {
public:
};
void test070201(Anima070201& animal) {
animal.speak();
}
int main() {
//Anima070201 animal;//没有重写纯虚函数,无法实例化
//Cat070202 cat;//没有重写纯虚函数,无法实例化
Anima070201* animal = new Cat070201;
animal->speak();
Cat070201 cat;
test070201(cat);
return 0;
}
使用多态时,如果子类属性指向堆内存,父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或纯虚析构
虚析构或纯虚析构的相同点:
虚析构或纯虚析构的区别:
虚析构语法 virtual ~类名(){}
纯虚析构语法 virtual ~类名()=0;
#include <iostream>
#include <string>
using namespace std;
class Anima070301 {
public:
Anima070301() {
cout << "Anima070301构造函数调用" << endl;
}
virtual ~Anima070301(){
cout << "Anima070301析构函数调用" << endl;
}
virtual void speak() = 0;
};
class Cat070301 :public Anima070301 {
public:
string *name;
Cat070301() {
cout << "Cat070301构造函数调用" << endl;
}
Cat070301(string newName) {
cout << "Cat070301有参构造函数调用" << endl;
this->name = new string(newName);
}
~Cat070301() {
if (this->name != NULL) {
delete name;
this->name = NULL;
}
cout << "Cat070301析构函数调用" << endl;
}
void speak() {
cout << *name <<"猫在说话" << endl;
}
};
// 纯虚析构函数
class Anima070302 {
public:
Anima070302() {
cout << "Anima070302构造函数调用" << endl;
}
// 春虚析构函数
virtual ~Anima070302() = 0;
virtual void speak() = 0;
};
Anima070302::~Anima070302() {
cout << "Anima070302析构函数调用" << endl;
}
class Cat070302 :public Anima070302 {
public:
string* name;
Cat070302() {
cout << "Cat070302构造函数调用" << endl;
}
Cat070302(string newName) {
cout << "Cat070302有参构造函数调用" << endl;
this->name = new string(newName);
}
~Cat070302() {
if (this->name != NULL) {
delete name;
this->name = NULL;
}
cout << "Cat070302析构函数调用" << endl;
}
void speak() {
cout << *name << "猫在说话" << endl;
}
};
void test070301(){
Anima070301* animal = new Cat070301("tom");
animal->speak();
// 父类指针析构不会调用子类析构函数,会导致内存泄露问题
delete animal;
}
void test070302() {
Anima070302* animal = new Cat070302("tom");
animal->speak();
// 父类指针析构不会调用子类析构函数,会导致内存泄露问题
delete animal;
}
int main() {
//test070301();
test070302();
return 0;
}
文件类型分为两种:
操作文件的三大类:
操作步骤如下:
#include <fstream>
ofstream ofs;
ofs.open(文件路径,打开方式);
ofs << '写入文本';
ofs.close();
文件打开方式如下:
文件打开方式 | 描述 |
---|---|
ios::app | 追加模式。所有写入都追加到文件末尾。 |
ios::ate | 文件打开后定位到文件末尾。 |
ios::in | 打开文件用于读取。 |
ios::out | 打开文件用于写入。 |
ios::trunc | 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。 |
ios::binary | 二进制方式 |
文件打开方式利用|(或)操作符号可以配合使用,例如以二进制方式写文件 ios::binary|ios::out
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
ofstream ofs;
ofs.open("test.txt",ios::out);
ofs << "张三" << endl;
ofs << "李四" << endl;
ofs.close();
return 0;
}
操作步骤如下:
#include <fstream>
ifstream ifs;
ifs.open(文件路径,打开方式);
ifs.close();
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open()) {
cout << "文件打开失败!";
return 0;
}
// 字符数组接收文本
char buff[1024] = {};
//while (ifs >> buff) {
// cout << buff << endl;
//}
// getline 读取文本
//while (ifs.getline(buff, sizeof(buff))) {
// cout << buff;
//}
// string接收文本
//string bufStr;
//while (getline(ifs, bufStr)) {
// cout << bufStr;
//}
// char字符接收文本
char ch;
while ((ch = ifs.get()) != EOF) {
cout << ch;
}
ifs.close();
return 0;
}
以ios::binary方式打开文件
二进制方式写文件主要利用流对象成员函数write
函数原型:ofstream& write(const char * buffer,int len)
参数说明:字符指针buffre指向内存中一段存储空间,len是读写的字节数
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class Person {
public:
char m_Name[64];
char m_Age;
};
void test0301(){
Person p = { "张三",10 };
ofstream ofs;
ofs.open("person.txt", ios::out | ios::binary);
ofs.write((const char*)&p, sizeof(Person));
ofs.close();
}
int main() {
test0301();
return 0;
}
以ios::binary方式打开文件
二进制方式写文件主要利用流对象成员函数read
函数原型:ofstream& read(const char * buffer,int len)
参数说明:字符指针buffre指向内存中一段存储空间,len是读写的字节数
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class Person01 {
public:
char m_Name[64];
int m_Age;
};
void test0401() {
ifstream ifs;
ifs.open("person.txt", ios::binary | ios::in);
if (!ifs.is_open()) {
return;
}
Person01 p;
ifs.read((char*)&p, sizeof(Person01));
cout << p.m_Name << endl;
cout << p.m_Age << endl;
}
int main() {
test0401();
return 0;
}