1 返回局部变量地址
- 不要返回局部变量的地址 local variables,这里的地址存储在栈区,函数运行结束后编译器自动释放
- 编译器可能会保留一次数据,所以第一次输出可能是对的
#include <iostream>
using namespace std;
int *func(){
int *a=10;
cout<<&a<<endl;
return a;
}
int main() {
int a=10;
int *p = NULL;
p = func();
cout<<*p<<endl;
cout<<"address:"<<endl;
cout<<p<<endl;
cout<<p<<endl;
cout<<*p<<endl;
cout<<*p<<endl;
return 0;
}
/*
0x61fdd8
-1163005939
address:
0x764040
0x764040
-1163005939
-1163005939
*/
形参也是一样的
- 放在堆区的可以返回,由程序员决定什么时候释放
#include <iostream>
using namespace std;
int *func(){
int *a=NULL;
a=new int(10);
cout<<&a<<endl;
cout<<&a<<endl;
return a;
}
int main() {
int a=10;
int *p = NULL;
p = func();
cout<<*p<<endl;
cout<<"address:"<<endl;
cout<<p<<endl;
cout<<p<<endl;
cout<<*p<<endl;
cout<<*p<<endl;
delete p;
cout<<*p<<endl;
return 0;
}
/*
0x61fdd8
0x61fdd8
10
address:
0x774040
0x774040
10
10
6495408
*/
2 引用&
- datatype &newname = originname
- 可以从中间引用数组
#include <iostream>
using namespace std;
void func(int arr[],int n){
for(int i=0;i<n;i++){
cout<<arr[i]<<endl;
}
}
int main() {
int arr[]={1,2,3,4,5,6,7,8};
int n=8;
func(&arr[n/3],n-n/3);
return 0;
}
/*
3
4
5
6
7
8
*/
- 必须初始化,且初始化以后不能更改引用为其他
#include <iostream>
using namespace std;
int main() {
int a = 10;
int c = 20;
//必须初始化
// int &b;
//初始化以后不能更改
int& b = a;
b = c; //这只是赋值,并不是更改引用
cout << b << endl;
return 0;
}
- 引用作为函数返回值
返回局部变量
下面这个程序在vscode中直接报错,在vs中可以运行
#include <iostream>
using namespace std;
int& func() {
int a = 10;
return a;
}
int main() {
int& ref = func();
cout << ref << endl;//第一次编译器做了保留
cout << ref << endl;//第二次错误,a的内存已经被释放
return 0;
}
/*
reference to local variable 'a' returned [-Wreturn-local-addr]
*/
- 函数调用作为左值
#include <iostream>
using namespace std;
int& func() {
static int a = 10; //静态变量,放在全局区,程序结束后才会被释放
return a;
}
int main() {
int& ref = func();
cout << ref << endl;
cout << ref << endl;
func() = 1000; //函数的调用可以作为左值
cout << ref << endl;
cout << ref << endl;
return 0;
}
/*
10
10
1000
1000
*/
- 引用&的本质是指针
#include <iostream>
using namespace std;
//内部发现是引用,自动转换成为 int*const ref = &a;
void func(int& ref) {
ref = 100; //自动转换为 *ref=100;
}
int main() {
int a = 10;
//自动转换为 int* const ref = &a;指针常量是指针不可修改,也对应引用不可修改
int& ref = a;
ref = 20; //内部发现ref是引用,自动帮我们进行转换为: *ref = 20;
cout << "a: " << a << endl;
cout << "ref: " << ref << endl;
func(a);
cout << "ref: " << ref << endl;
return 0;
}
/*
a: 20
ref: 20
ref: 100
*/
- 常量引用
#include <iostream>
using namespace std;
int main() {
//常量引用
//下面这行语句会报错
// initial value of reference to non-const must be an lvalue
// int &ref =10;//引用必须是一块合法的空间
//下面这个表达没有报错是因为编译器自动进行了一个操作,可以认为:
// int tmp=10;const int &ref = tmp;
const int& ref = 10;
cout << ref << endl;
return 0;
}
3 函数 function
- 函数形参默认值,如果某个形参有默认参数,后面的也必须有默认参数
错误写法
void func(int a, int b = 10, int c)
正确写法
#include <iostream>
using namespace std;
int func(int a, int b = 10, int c = 30) {
return a + b + c;
}
int main() {
int res;
res = func(5); //可以只传入没有默认参数的
res = func(5, 30); //编译对b的值使用用户传入的30
return 0;
}
- 函数声明有默认参数,函数实现就不能再有,不然后有歧义,报错
//会报错
//函数声明和函数实现中形参的默认参数只能有一个有
int func(int a, int b = 10, int c = 30);
int func(int a, int b = 10, int c = 30) {
return a + b + c;
}
/*
default argument given for parameter 2 of 'int func(int, int, int)' [-fpermissive]
*/
- 占位参数
#include <iostream>
using namespace std;
//第二个是占位参数
void func(int a, int) {
cout << "Hello world!" << endl;
}
int main() {
func(10, 10); //第二个占位参数也要传入初始值
return 0;
}
也可以有默认参数
#include <iostream>
using namespace std;
//第二个是占位参数,也可以有默认参数
void func(int a, int = 10) {
cout << "Hello world!" << endl;
}
int main() {
func(10); //第二个占位参数可以不用传入初始值
return 0;
}
4 函数重载
- 函数名可以相同,提高复用率
- 函数名称相同
- 参数类型不同,或者个数不同,或者顺序不同
- 返回类型不可以作为重载条件
#include <iostream>
using namespace std;
void func() {
cout << "func1" << endl;
}
void func(int a) {
cout << "func2" << endl;
}
void func(int a, double b) {
cout << "func3" << endl;
}
void func(double a, int b) {
cout << "func4" << endl;
}
//返回类型不可以作为重载的条件
/*
int func(double a,int b){
cout<<"func5"<<endl;
}
*/
int main() {
func();
func(10);
func(10, 3.14);
func(3.14, 10);
return 0;
}
/*
func1
func2
func3
func4
*/
注意事项
- 遇到引用
#include <iostream>
using namespace std;
void func(int& a) { // int &a=10不合法,所以不会运行这个
cout << "func1" << endl;
}
void func(const int& a) {
cout << "func2" << endl;
}
int main() {
int a = 10;
func(a);
func(10);
return 0;
}
/*
func1
func2
*/
- 遇到默认参数
#include <iostream>
using namespace std;
void func(int a, int b = 10) { // int &a=10不合法,所以不会运行这个
cout << "func1" << endl;
}
void func(int a) {
cout << "func2" << endl;
}
int main() {
func(10); //有二义性,会报错
return 0;
}
所以要尽量避免产生歧义
5 类和对象
1) 类
- class和struct的唯一区别是默认权限不一样,前者是private,后者是public
2) 构造函数和析构函数
#include <iostream>
#include <string.h>
using namespace std;
//对象的初始化和清理
class Person {
private:
/* data */
public:
Person(/* args */);
~Person();
};
/*构造函数
没有返回值,不用写void
函数名与类名相同
可以有参数,可以重载
创建对象的时候,构造函数会自动调用,而且只调用一次*/
Person::Person(/* args */) {
cout << "Person 构造函数的调用" << endl;
}
/*析构函数
没有返回值 不用写void
函数名与类名相同,前面加~
没有参数,不能重载
对象销毁前会自动调用,无需手动调用,而且只会调用一次
*/
Person::~Person() {
cout << "Person 析构函数的调用" << endl;
}
int main() {
Person p1;
return 0;
}
/*
Person 构造函数的调用
Person 析构函数的调用
*/
构造函数
调用方式
#include <iostream>
#include <string.h>
using namespace std;
/*构造函数分类
按照参数分类 有参和无参构造,无参构造又称为默认构造
按照类型 普通构造和拷贝构造
*/
class Person {
private:
int age;
public:
Person(/* args */);
Person(int);
Person(const Person&);
~Person();
};
/*无参构造*/
Person::Person(/* args */) {
cout << "默认构造函数的调用" << endl;
}
/*有参函数构造*/
Person::Person(int a) {
age = a;
cout << "有参函数的调用" << endl;
}
/*拷贝函数构造
使用const防止修改传入的数据,使用引用传入*/
Person::Person(const Person& p) {
age = p.age;
cout << "拷贝函数的调用" << endl;
}
/*默认析构函数
*/
Person::~Person() {
cout << "默认析构函数的调用" << endl;
}
int main() {
//调用构造函数
// 1 括号法,常用
//有参函数的调用
Person p1(10);
//使用默认构造不能加括号,不然ide认为这是一个函数声明
//报错:'Person p1()' redeclared as different kind of symbol
// Person p1();
// 2 显示法
//这是在声明一个匿名对象,马上就会被系统清理
// Person(10);
// Person p2(10);
//有参函数的调用
Person p2 = Person(10);
// 拷贝函数的调用
Person p3 = Person(p2);
// 3 . 隐式转换法
Person p4 = 10; // Person p4 = Person(10);//有参函数的调用
Person p5 = p4; // Person p5=Person(p4);//拷贝函数的调用
// notice
// 不能利用拷贝构造函数初始化匿名对象,编译器会认为是对象声明
// Person p6(p5);
return 0;
}
调用时机
#include <iostream>
#include <string.h>
using namespace std;
/*构造函数分类
按照参数分类 有参和无参构造,无参构造又称为默认构造
按照类型 普通构造和拷贝构造
*/
class Person {
private:
int age;
public:
Person(/* args */);
Person(int);
Person(const Person&);
~Person();
};
/*无参构造*/
Person::Person(/* args */) {
cout << "默认构造函数的调用" << endl;
}
/*有参函数构造*/
Person::Person(int a) {
age = a;
cout << "有参函数的调用" << endl;
}
/*拷贝函数构造
使用const防止修改传入的数据,使用引用传入*/
Person::Person(const Person& p) {
age = p.age;
cout << "拷贝函数的调用" << endl;
}
/*默认析构函数
*/
Person::~Person() {
cout << "默认析构函数的调用" << endl;
}
void func1(Person p) {
cout << &p << endl;
cout << "func1 函数被调用" << endl;
}
Person func2() {
cout << "func2 函数被调用" << endl;
Person p1;
cout << &p1 << endl;
return p1;
}
int main() {
//调用构造函数的时机
Person p1(10);
cout << &p1 << endl;
func1(p1);
cout << &p1 << endl;
Person p2 = func2();
cout << &p2 << endl;
return 0;
}
调用条件
声明一个对象时,系统会自动提供至少三个函数,默认构造函数,默认拷贝函数,默认析构函数
如果声明了有参函数,那么不再提供默认构造函数,如果声明了拷贝函数,那么不再提供默认构造和默认拷贝函数
深拷贝和浅拷贝
#include <iostream>
#include <string.h>
using namespace std;
/*构造函数
深拷贝与浅拷贝
使用编译器默认的拷贝函数会进行浅拷贝
浅拷贝其实就是使用同一个内存空间
*/
class Person {
private:
int age;
int* hight = NULL;
public:
Person(/* args */);
Person(int, int);
Person(const Person&);
void ShowInfo();
~Person();
};
/*无参构造*/
Person::Person(/* args */) {
cout << "默认构造函数的调用" << endl;
}
/*有参函数构造*/
Person::Person(int a, int h) {
age = a;
hight = new int(h); //声明对象在堆区,由程序员手动释放
cout << hight << endl;
cout << "有参函数的调用" << endl;
}
/*拷贝函数构造
使用const防止修改传入的数据,使用引用传入*/
Person::Person(const Person& p) {
age = p.age;
//编译器默认的这是这样拷贝的
// hight = p.hight;
// cout<<hight<<endl;
// 我们自己进行深拷贝操作
hight = new int(*p.hight); //开辟另一个空间
*hight = *p.hight;
cout << hight << endl;
cout << "拷贝函数的调用" << endl;
}
void Person::ShowInfo() {
cout << "年龄是: " << age << endl;
cout << "身高是: " << *hight << endl;
}
/*默认析构函数
*/
Person::~Person() {
//将堆区开辟的空间进行释放
// p2 先进行释放,然后p1再来一次释放,造成重复释放报错
//浅拷贝造成的问题:内存重复释放
if(hight) {
delete hight;
hight = NULL; //防止野指针,进行置空
}
cout << "默认析构函数的调用" << endl;
}
int main() {
Person p1(18, 170);
p1.ShowInfo();
Person p2(p1);
p2.ShowInfo();
return 0;
}
列表初始化
#include <iostream>
#include <string.h>
using namespace std;
class Person {
private:
int a;
int b;
int c;
public:
Person(int a1, int b1, int c1);
void ShowSum();
~Person();
};
//列表初始化
Person::Person(int a1, int b1, int c1)
: a(a1), b(b1), c(c1) {
}
void Person::ShowSum() {
cout << a + b + c << endl;
}
Person::~Person() {
}
int main() {
Person p1(10, 20, 30);
p1.ShowSum();
return 0;
}
/*
60
*/
类作为类成员
注意回收次序
#include <iostream>
#include <string.h>
using namespace std;
class Phone {
public:
Phone(string name) {
PhoneName = name;
cout << "Phone 构造函数" << endl;
}
~Phone() {
cout << "Phone 析构函数" << endl;
}
void showname() {
cout << PhoneName;
}
private:
string PhoneName;
};
class Person {
public:
Person(string name, string Phname)
: Name(name), Mphone(Phname) {
cout << "person 构造函数" << endl;
}
~Person() {
cout << "Person 析构函数" << endl;
}
void show() {
cout << Name << " use ";
Mphone.showname();
cout << " telephone!" << endl;
}
private:
string Name;
Phone Mphone;
};
int main() {
string name = "张三";
string Phname = "HONOR";
Person p1(name, Phname);
p1.show();
return 0;
}
/*
Phone 构造函数
person 构造函数
张三 use HONOR telephone!
Person 析构函数
Phone 析构函数
*/
静态成员变量
#include <iostream>
#include <string.h>
using namespace std;
/*静态成员变量特点
1. 在编译阶段分配内存
2. 类内声明,类外初始化
3. 所有对象共享同一份数据*/
class Person {
private:
//静态成员变量依然有访问权限
static int age;
public:
void showage() {
cout << age << endl;
}
};
//类内声明,在类外初始化
int Person::age = 100;
int main() {
Person p1;
p1.showage();
return 0;
}
静态成员函数
#include <iostream>
#include <string.h>
using namespace std;
/*静态成员函数特点
1. 所有对象共享同一个函数
2. 静态成员函数只能访问静态成员变量*/
class Person {
private:
//静态成员变量依然有访问权限
static int age;
int hight;
public:
static void showage() {
// 无法访问非静态成员变量,
// 非静态成员变量属于不同的对象,IDE无法分辨程序是想访问哪一个对象的变量
// cout<<hight<<endl;
cout << age << endl;
cout << "静态成员函数的调用" << endl;
}
};
//类内声明,在类外初始化
int Person::age = 100;
int main() {
Person p1;
//访问方式
// 1. 通过对象来访问
p1.showage();
// 2. 通过类名访问
// 所有对象共享同一个变量的地址
Person::showage();
return 0;
}
成员函数和成员变量分开存储
#include <iostream>
#include <string.h>
using namespace std;
/*成员变量和成员函数是分开存储的*/
class None {
};
class Person1 {
private:
//静态成员变量依然有访问权限
static int age;
//非静态成员变量
int hight;
};
//类内声明,在类外初始化
int Person1::age = 100;
class Person2 {
private:
int hight;
};
class Person3 {
private:
int hight;
public:
void showhight() {
cout << hight << endl;
}
};
int main() {
None n;
//输出空对象所占用的内存大小
// c++编译器会个每一个空对象分配1个字节的空间,为了区分空对象占用内存的位置
// 每一个空对象都有一个独一无二的位置
cout << "size of n " << sizeof(n) << endl;
Person1 p1;
//验证静态和非静态是分开存储的
/*
size of p1 4
size of p2 4
*/
cout << "size of p1 " << sizeof(p1) << endl;
Person2 p2;
cout << "size of p2 " << sizeof(p2) << endl;
// size of p3 4
// 成员函数也不是存数在类里面
Person3 p3;
cout << "size of p3 " << sizeof(p3) << endl;
return 0;
}
this指针
#include <iostream>
#include <string.h>
using namespace std;
/*this指针
1. 解决名称冲突
2. 返回对象本身*/
class Person {
public:
//假如我们这样写就会发生名称重复
// Person(int Age){
// Age = Age;
// }
//使用this来解决
Person(int Age) {
// this指针指向被调用的成员函数的所属的对象
this->Age = Age;
}
void showAge() {
cout << Age << endl;
}
//返回的对象就是他调用的对象本身
// 下面两种返回类型得到的结果是不一样的
// 1 不使用引用,这样返回的已经不是对象本身了,而是对象的一个副本
// Person AddAge(const Person &p){
// this->Age+=p.Age;
// return *this;
// }
// 2. 使用引用
Person& AddAge(const Person& p) {
this->Age += p.Age;
cout << Age << endl;
return *this;
}
private:
int Age;
};
int main() {
Person p1(18);
Person p2(18);
p1.showAge();
p2.showAge();
p2.AddAge(p1);
p2.showAge();
//链式编程思想
p2.AddAge(p1).AddAge(p1).AddAge(p1);
p2.showAge();
return 0;
}
/*
18
18
36
36
54
72
90
90
*/
空指针访问成员函数
#include <iostream>
#include <string.h>
using namespace std;
/*我们允许使用空指针访问成员函数*/
class Person {
public:
void showclasname() {
cout << "this is Person class" << endl;
}
void showage() {
//其实默认都加了一个this指针,即应该是this->Age这样,
//使用空指针时,this->Age就会报错,上面的函数就没事
// 一般解决办法,使用this之前先进行判断
if(!this)
return;
cout << "age = " << Age << endl;
}
Person() {
Age = 18;
}
private:
int Age;
};
int main() {
//声明一个空指针
Person* p = NULL;
p->showclasname();
p->showage();
return 0;
}
cosnt修饰成员函数
#include <iostream>
#include <string.h>
using namespace std;
/*常函数 常对象*/
class Person {
public:
void showclasname() {
cout << "this is Person class" << endl;
}
void showage() const {
//在成员函数后面写const修饰的是 this指针,让指针指向的值也不可以被修改
// 报错语句
// Age = 30;
cout << "age = " << Age << endl;
cout << "Hight = " << Hight << endl;
//常函数中也可以修改的值
Hight = 175;
cout << "Hight = " << Hight << endl;
}
Person() {
Age = 18;
Hight = 170;
}
//测试常对象不能修改变量值
void chagevalue(int num) {
Age = num;
}
private:
int Age;
//特殊变量,在常函数中也可以修改他的值
mutable int Hight;
};
int main() {
//声明一个空指针
Person p;
p.showclasname();
p.showage();
//在对象前面加上const 变成常对象
Person p1;
//常对象只能调用常函数,
p1.showage();
// 不可以调用普通成员函数,// 因为普通成员函数可以修改成员属性
p1.chagevalue(25);
p1.showage();
return 0;
}
3) 友元函数
全局函数作为友元
#include <iostream>
#include <string.h>
using namespace std;
/*友元函数*/
class House {
//写在最上面,告诉IDE这是一个全局函数
friend void confidant(House&);
private:
string BedRoom;
public:
string SittingRoom;
House() {
BedRoom = "卧室";
SittingRoom = "客厅";
}
};
void confidant(House& h) {
cout << "闺蜜正在访问" << h.SittingRoom << endl;
cout << "闺蜜正在访问" << h.BedRoom << endl;
}
int main() {
House h;
confidant(h);
return 0;
}
友元类
#include <iostream>
#include <string.h>
using namespace std;
/*友元类
可以访问私有*/
class House {
//类作为友元
friend class Confidant;
private:
string BedRoom;
public:
string SittingRoom;
House() {
BedRoom = "卧室";
SittingRoom = "客厅";
}
};
class Confidant {
public:
void visit(House& h) {
cout << "闺蜜正在访问 " << h.SittingRoom << endl;
cout << "闺蜜正在访问 " << h.BedRoom << endl;
}
};
int main() {
Confidant c;
House h;
c.visit(h);
return 0;
}
成员函数作为友元函数
bug
#include <iostream>
#include <string.h>
using namespace std;
/*友元成员函数
可以访问私有*/
//先声明一下
class Confidant;
class House {
//类成员函数作为友元
// friend void Confidant::visit(House& h);
friend void Confidant::visit(House& h);
private:
string BedRoom;
public:
string SittingRoom;
House() {
BedRoom = "卧室";
SittingRoom = "客厅";
}
};
class Confidant {
public:
void visit(House& h) {
cout << "闺蜜正在访问 " << h.SittingRoom << endl;
cout << "闺蜜正在访问 " << h.BedRoom << endl;
}
};
int main() {
Confidant c;
House h;
c.visit(h);
return 0;
}
4) 运算符重载
+
bug
#include <iostream>
#include <string.h>
using namespace std;
/*运算符重载
*/
class Person {
public:
int a;
int b;
// int operator+(const Person& p) {
// return a + p.a;
// }
//运算符重载的函数重载
// 这里不知道为什么老师的可以我的不可以,是ide的原因吗
int operator+(int num1, int num) {
return a + num1 + num;
}
int operator+(int num) {
return a + num;
}
};
int main() {
Person p1;
p1.a = 10;
p1.b = 20;
Person p2;
p2.a = 30;
p2.b = 40;
//这两个表达是一样的,前者是简化的版本
// cout << p1 + p2 << endl;
// cout << p1.operator+(p2) << endl;
cout << p1.operator+(10) << endl;
cout << p1 + 10 << endl;
// cout<<p1.operator+(p2,10);
cout << p1 + 10 + 10 << endl;
return 0;
}
<<
#include <iostream>
#include <string.h>
using namespace std;
class Person {
friend ostream& operator<<(ostream&, Person&);
public:
void setvalue(int num1, int num2) {
a = num1;
b = num2;
}
private:
int a;
int b;
//通常不用成员函数重载<<运算符,使用全局函数,
//不然cout不会在左边
};
//注意返回值如果是void的话我们使用endl就会报错
//注意要用引用作为返回值,不然返回的是副本
ostream& operator<<(ostream& out, Person& p) {
out << p.a << " and " << p.b;
return out;
};
int main() {
Person p;
p.setvalue(100, 120);
cout << p << endl;
return 0;
}
/*
100 and 120
*/
++
#include <iostream>
#include <string.h>
using namespace std;
class Integer {
friend ostream& operator<<(ostream&, Integer&);
private:
int Num;
public:
Integer() {
Num = 10;
}
// 前置++重载
Integer& operator++() {
++Num;
return *this;
}
// 后置++重载
// int代表的是一个占位参数,可以用于区分前置和后置
Integer& operator++(int) {
//记录这个值
Integer* tmp = NULL;
tmp = new Integer;
*tmp = *this;
// 进行递增
Num++;
// 返回记录的值
return *tmp;
}
};
ostream& operator<<(ostream& out, Integer& i) {
out << i.Num;
return out;
}
int main() {
Integer i;
cout << i << endl;
cout << ++i << endl;
cout << i++ << endl;
cout << i << endl;
return 0;
}
/*
10
11
11
12
*/
=
#include <iostream>
#include <string.h>
using namespace std;
/*
系统会自动给出一个赋值函数,但是涉及到深浅拷贝的问题
使用系统默认的这个函数回到导致后期重复释放的问题*/
class Person {
friend ostream& operator<<(ostream& out, Person& i);
private:
//声明在堆区, 所以就涉及手动释放的问题
int* Age = NULL;
public:
Person(int age) {
Age = new int(age);
}
//使用系统默认的这个函数回到导致后期重复释放的问题
Person& operator=(Person& p) {
//编译器提供的是浅拷贝
// Age = p.age;
//先判断属性是否是在堆区,在的话清楚干净,然后再深拷贝
if(Age) {
delete Age;
Age = NULL;
}
//深拷贝
Age = new int(*p.Age);
return *this;
}
~Person() {
if(Age) { //如果不为空,我们就回收它
delete Age;
Age = NULL;
}
}
};
ostream& operator<<(ostream& out, Person& i) {
out << *i.Age;
return out;
}
int main() {
Person p1(18);
Person p2(20);
Person p3(30);
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
p3 = p2 = p1;
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
return 0;
}
/*
18
20
30
18
18
18
*/
==
#include <cstdbool>
#include <iostream>
#include <string.h>
using namespace std;
/*
关系运算符*/
class Person {
private:
int Age;
string Name;
public:
Person(int age, string name) {
Age = age;
Name = name;
}
bool operator==(const Person& p) const {
if(Age == p.Age)
return true;
return false;
}
};
bool func(const Person& p1, const Person& p2) {
if(p1 == p2)
return true;
return false;
}
int main() {
Person p1(18, "Tom");
Person p2(20, "Jake");
if(func(p1, p2))
cout << "the ages are same" << endl;
else
cout << "It's different" << endl;
return 0;
}
重载成员函数
#include <iostream>
#include <string.h>
using namespace std;
/*
函数调用重载运算符*/
//使用非常灵活
class Print {
public:
void operator()(string context) {
cout << context << endl;
}
};
class Add {
public:
int operator()(int num1, int num2) {
return num1 + num2;
}
};
int main() {
Print p;
//和函数使用非常像,也称为仿函数
p("Hello world");
Add a;
cout << a(5, 2) << endl;
//使用匿名对象,执行完立即消失
cout << Add()(100, 100) << endl;
return 0;
}
/*
Hello world
7
200
*/
5) 继承
继承方式的特点
继承对象模型
#include <iostream>
#include <string.h>
using namespace std;
/*
继承*/
//所有的都被继承了,即使是基类中的private
class Base {
public:
int a;
protected:
int b;
private:
int c;
};
class Son1 : public Base {
private:
int d;
};
int main() {
Son1 s;
cout << "the size of s " << sizeof(s) << endl;
return 0;
}
/*
16
*/
构造和析构的顺序
#include <iostream>
#include <string.h>
using namespace std;
/*
继承*/
//所有的都被继承了,即使是基类中的private
class Base {
public:
int a;
Base() {
cout << "Base 构造函数" << endl;
}
~Base() {
cout << "Base 析构函数" << endl;
}
protected:
int b;
private:
int c;
};
class Son1 : public Base {
public:
Son1() {
cout << "Son1 构造函数" << endl;
}
~Son1() {
cout << "Son1 析构函数" << endl;
}
private:
int d;
};
int main() {
Son1 s;
return 0;
}
/*
Base 构造函数
Son1 构造函数
Son1 析构函数
Base 析构函数
*/
继承同名的处理方式
#include <iostream>
#include <string.h>
using namespace std;
/*
继承*/
// 同名时编译器会隐藏基类中的所有同名属性或者函数
class Base {
public:
int a = 100;
void func() {
cout << "Base func函数调用" << endl;
}
protected:
int b;
private:
int c;
};
class Son1 : public Base {
public:
int a = 200;
void func() {
cout << "Son1 func函数调用" << endl;
}
private:
int d;
};
int main() {
Son1 s;
cout << s.a << endl;
//同名时想要访问到基类中的成员变量,加上作用域
cout << s.Base::a << endl;
//函数的同名
s.func();
s.Base::func();
return 0;
}
同名静态成员的处理方式
#include <iostream>
#include <string.h>
using namespace std;
/*
继承*/
// 同名时编译器会隐藏基类中的所有同名属性或者函数
class Base {
public:
//类内声明
static int a;
};
int Base::a = 100;
class Son1 : public Base {
public:
static int a;
};
int Son1::a = 200;
int main() {
Son1 s;
//通过对象访问
cout << s.a << endl;
//同名时想要访问到基类中的成员变量,加上作用域
cout << s.Base::a << endl;
//通过类名访问
cout << Son1::a << endl;
cout << Son1::Base::a << endl;
return 0;
}
/*
静态函数也是一样的
*/
多继承语法
#include <iostream>
#include <string.h>
using namespace std;
/*
继承*/
// 同名时编译器会隐藏基类中的所有同名属性或者函数
class Base1 {
public:
//类内声明
static int a;
};
int Base1::a = 100;
class Base2 {
public:
int b;
};
class Son1 : public Base1, public Base2 {
public:
static int a;
};
int Son1::a = 200;
int main() {
Son1 s;
cout << sizeof(s) << endl;
return 0;
}
/*
语法: class 子类: 继承方式:基类,继承方式,基类
多继承容易出现同名问题,使用的时候说明作用域就好
*/
虚继承解决菱形继承问题
/*
继承*/
class Animal {
public:
int Age;
};
class Sheep : public Animal {
public:
Sheep() {
Age = 10;
}
};
class Camel : public Animal {
public:
Camel() {
Age = 3;
}
};
class Caonima : public Sheep, public Camel {
};
// 为了解决多余继承问题
//使用虚继承
class Newone : virtual public Sheep, virtual public Camel {
};
int main() {
Caonima c;
cout << sizeof(c) << endl; // 8 说明继承了两份
Newone n;
cout << sizeof(n) << endl;
return 0;
}
6)多态
基本语法
#include <iostream>
#include <string.h>
using namespace std;
/*多态
1. 有继承关系
2. 派生类重写基类的函数,派生类中可写virtual也可以不写,基类必须写*/
class Animal {
public:
//
// void speak() {
// cout << "动物在说话" << endl;
// }
//写成虚函数
virtual void speak() {
cout << "动物在说话" << endl;
}
};
class Sheep : public Animal {
public:
void speak() {
cout << "小羊在说话" << endl;
}
};
class Camel : public Animal {
public:
void speak() {
cout << "骆驼在说话" << endl;
}
};
//执行说话的函数
//地址早帮定,在编译阶段就已经确定了函数的地址
// 如果想让不同的动物说话,就不能一开始就确定函数地址
//需要运行阶段再帮定
void doSpeak(Animal& animal) {
animal.speak();
};
int main() {
Sheep sheep;
doSpeak(sheep);
return 0;
}
计算器(案例)
#include <iostream>
#include <string.h>
using namespace std;
/*多态*/
//普通写法
class Calculator {
public:
void set(int a, int b) {
n1 = a;
n2 = b;
}
int Result(string oper) {
if(oper == "+")
return n1 + n2;
else if(oper == "-")
return n1 - n2;
else if(oper == "*")
return n1 * n2;
return -999; //没有符合的情况
//如果想要添加新的功能,需要修改源码
}
private:
int n1, n2;
};
//多态写法
//结构清晰
//可读性强
// 方便扩展和维护
class AbstractCalculator {
public:
virtual int Result() {
return 0;
}
void SetValue(int a, int b) {
n1 = a;
n2 = b;
}
protected:
int n1, n2;
};
//加法类
class AddCalculator : public AbstractCalculator {
public:
virtual int Result() {
return n1 + n2;
}
};
// 减法类
class MinusCalculator : public AbstractCalculator {
public:
virtual int Result() {
return n1 - n2;
}
};
//乘法类
class MultipleCalculator : public AbstractCalculator {
public:
virtual int Result() {
return n1 - n2;
}
};
int main() {
Calculator c;
c.set(10, 12);
cout << c.Result("+") << endl;
//多态
// 基类的指针或者引用指向派生类对象
AbstractCalculator* abc = new AddCalculator;
abc->SetValue(10, 12);
cout << abc->Result() << endl;
//创建在堆区,记得手动释放
delete abc;
//现在想创建一个减法类
// abc在堆区的空间已经被释放,但是地址依然在
abc = new MinusCalculator;
abc->SetValue(10, 12);
cout << abc->Result() << endl;
return 0;
}
纯虚函数和抽象类
#include <iostream>
#include <string.h>
using namespace std;
/*多态*/
//纯虚函数和抽象类
class Base {
public:
/*纯虚函数的基本语法
只要有一个纯虚函数,这个类称为抽象类
特点:
1. 无法实例化对象
2. 抽象类的派生类必须重写基类中的abstract function,不然也是继承的抽象类*/
virtual void func() = 0;
};
class Son : public Base {
public:
virtual void func() {
cout << "function of func has been used" << endl;
}
};
int main() {
// object of abstract class type "Base" is not allowed:
// function "Base::func" is a pure virtual function
// Base s;
Son s;
s.func();
return 0;
}
制作饮品(案例)
#include <iostream>
#include <string.h>
using namespace std;
/*多态*/
//纯虚函数和抽象类
class AbstractDrinking {
public:
virtual void Biol() = 0;
virtual void Brew() = 0;
virtual void PourInCup() = 0;
virtual void PutSomthing() = 0;
void MakeDrink() {
Biol();
Brew();
PourInCup();
PutSomthing();
}
};
//制作coffe
class Coffee : public AbstractDrinking {
public:
virtual void Biol() {
cout << "烧开农夫山泉" << endl;
}
virtual void Brew() {
cout << "冲泡咖啡" << endl;
}
virtual void PourInCup() {
cout << "倒入杯子中" << endl;
}
virtual void PutSomthing() {
cout << "加入牛奶" << endl;
}
};
//制作tea
class Tea : public AbstractDrinking {
public:
virtual void Biol() {
cout << "烧开井水" << endl;
}
virtual void Brew() {
cout << "冲泡茶叶" << endl;
}
virtual void PourInCup() {
cout << "倒入杯子中" << endl;
}
virtual void PutSomthing() {
cout << "加入枸杞" << endl;
}
};
// 制作函数
void DoWork(AbstractDrinking* abs) {
abs->MakeDrink();
//手动清除
delete abs;
}
int main() {
DoWork(new Coffee);
cout << "-----------" << endl;
DoWork(new Tea);
return 0;
}
虚析构和纯虚析构
#include <iostream>
#include <string.h>
using namespace std;
/*多态*/
/*1. 用于解决无法通过基类释放派生类对象
2. 如果没有堆区数据,可以不写虚析构或者纯虚析构函数
3. 有了纯虚析构那么也是虚类,不能实例化对象*/
class Animal {
public:
//纯虚函数
virtual void Speak() = 0;
Animal() {
cout << "Animal 构造函数调用" << endl;
}
// ~Animal(){
// cout<<"Animal 析构函数调用"<<endl;
// }
// 1. 利用虚析构函数解决基类指针无法调用派生类的析构函数
// virtual ~Animal(){
// cout<<"Animal 虚析构函数调用"<<endl;
// }
// 2. 利用纯虚析构
virtual ~Animal() = 0;
};
//纯虚析构如果声明一定要实现
//另外有了纯虚析构那么也是虚类,不能实例化对象
Animal::~Animal() {
cout << "Animal 纯虚析构函数调用" << endl;
}
class Cat : public Animal {
public:
virtual void Speak() {
cout << *Name << " 小猫在说话" << endl;
}
//析构函数
Cat(string name) {
cout << "Cat 构造函数调用" << endl;
Name = new string(name);
}
~Cat() {
if(Name) {
cout << "Cat 析构函数调用" << endl;
delete Name;
Name = NULL;
}
}
private:
string* Name = NULL;
};
int main() {
Animal* animal = new Cat("Tom");
animal->Speak();
//基类的指针在析构的时候无法调用派生类中的析构函数
//导致子类如果有数据在堆中,那么就不会被释放,导致内存泄露
delete animal;
return 0;
}
电脑组装 (案例)
#include <iostream>
using namespace std;
/*多态*/
class CPU {
public:
virtual void calculator() = 0;
};
class GPU {
public:
virtual void display() = 0;
};
class Memory {
public:
virtual void storage() = 0;
};
class Computer {
public:
Computer(CPU* c,
GPU* g,
Memory* m) {
cpu = c;
gpu = g;
memory = m;
}
//提供工作函数
void Work() {
//让不同的零件调用不同的借口开始工作
cpu->calculator();
gpu->display();
memory->storage();
}
//提供析构函数释放传入的3个电脑零件
~Computer() {
if(cpu) {
delete cpu;
cpu = NULL;
}
if(gpu) {
delete gpu;
gpu = NULL;
}
if(memory) {
delete memory;
memory = NULL;
}
}
private:
CPU* cpu = NULL;
GPU* gpu = NULL;
Memory* memory = NULL;
};
// Inter厂商
class InterCPU : public CPU {
public:
void calculator() {
cout << "Inter 的 GPU 开始计算了" << endl;
}
};
class InterGPU : public GPU {
public:
void display() {
cout << "Inter 的 GPU 开始显示了" << endl;
}
};
class InterMemory : public Memory {
public:
void storage() {
cout << "Inter 的 Memory 开始存储了" << endl;
}
};
// Lenovo厂商
class LenovoCPU : public CPU {
public:
void calculator() {
cout << "Lenovo 的 GPU 开始计算了" << endl;
}
};
class LenovoGPU : public GPU {
public:
void display() {
cout << "Lenovo 的 GPU 开始显示了" << endl;
}
};
class LenovoMemory : public Memory {
public:
void storage() {
cout << "Lenovo 的 Memory 开始存储了" << endl;
}
};
int main() {
//第一台电脑的零件
CPU* interCpu = new InterCPU;
GPU* interGpu = new InterGPU;
Memory* interMemory = new InterMemory;
//创建第一台电脑
Computer* computer = new Computer(interCpu, interGpu, interMemory);
computer->Work();
//释放
delete computer;
cout << "-------------" << endl;
//组装第二台电脑
Computer* computer1 = new Computer(new LenovoCPU, new LenovoGPU, new LenovoMemory);
computer1->Work();
delete computer1;
cout << "-------------" << endl;
//创建第3台电脑, 混合
Computer* computer2 = new Computer(new InterCPU, new LenovoGPU, new InterMemory);
computer2->Work();
delete computer2;
return 0;
}
6 文件
1)写
#include <fstream>
#include <iostream>
using namespace std;
/*文件*/
int main() {
ofstream ofs;
ofs.open("file.txt", ios::out);
ofs << "这是我想要存储在文件中的文本" << endl;
ofs << "这个也是想要保存的" << endl;
ofs.close();
return 0;
}
2) 读
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
/*文件*/
int main() {
ifstream ifs;
ifs.open("file.txt", ios::in);
//判断是否正确打开
if(!ifs.is_open()) {
cout << "文件没有正确打开" << endl;
return 0;
}
//读数据
// way 1
// char buf[1024]={0};
// while (ifs>>buf)
// {
// cout<<buf<<endl;
// }
// way 2
// char buf[1024]={0};
// while (ifs.getline(buf,sizeof(buf)))
// {
// cout<<buf<<endl;
// }
// way 3
// string buf;
// while(getline(ifs,buf)){
// cout<<buf<<endl;
// }
// way 4
char c;
while((c = ifs.get()) != EOF) {
cout << c;
}
ifs.close();
return 0;
}
3)二进制写
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
char Name[64];
int Age;
};
/*文件*/
int main() {
// expression 1
// ofstream ofs;
// ofs.open("file.txt",ios::out | ios::binary);
// expression 2
ofstream ofs("file.txt", ios::out | ios::binary);
Person p = {"张三", 18};
//写入文件
ofs.write((const char*)&p, sizeof(p));
ofs.close();
return 0;
}
4)二进制读
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
char Name[64];
int Age;
};
/*文件*/
int main() {
ifstream ifs;
ifs.open("file.txt", ios::in | ios::binary);
if(!ifs.is_open()) {
cout << "文件打开失败" << endl;
return 0;
}
Person p;
ifs.read((char*)&p, sizeof(Person));
cout << "姓名:" << p.Name << " 年龄: " << p.Age << endl;
ifs.close();
return 0;
}
7 模板
1)基本语法
#include <iostream>
using namespace std;
template <typename Type>
void Swap(Type& a, Type& b) {
Type tmp = a;
a = b;
b = tmp;
}
int main() {
int a = 12;
int b = 20;
cout << a << " " << b << endl;
// 1. 自动类型推导
Swap(a, b);
cout << a << " " << b << endl;
double c = 2.345345;
double d = 5.232423;
cout << c << " " << d << endl;
// 2. 显示指定类型
Swap<double>(c, d);
cout << c << " " << d << endl;
return 0;
}
2)函数模板
没有参数时
#include <iostream>
using namespace std;
template <typename Type>
void func() {
cout << "func 函数被调用" << endl;
}
int main() {
//没有参数类型的话没有办法自动推导
// func();
//可以随便给出一个类型
func<int>();
func<double>();
return 0;
}
普通函数和函数模板的区别
#include <iostream>
using namespace std;
template <typename Type>
void func1(Type a, Type b) {
cout << a + b << endl;
}
void func2(int a, int b) {
cout << a + b << endl;
}
int main() {
//整型和字符型是可以自动发生隐式转换的
int a = 20;
char ch = 'a';
//无法被推导出一致的参数类型,多一报错
// func1(a,ch);//这一句无法被运行
//我么可以使用显式指定类型
func1<int>(a, ch);
func2(a, ch);
return 0;
}
普通函数和函数模板的调用规则
#include <iostream>
using namespace std;
/*调用规则
1. 如果函数模板和普通模板都可以实现那么优先调用普通函数
2.通过空模板参数列表强制调用 函数模板
3. 函数模板可以发生函数重载
4. 如果函数模板可以产生更好的匹配,那么优先调用函数模板*/
template <typename Type>
void func(Type a, Type b) {
cout << a + b << endl;
cout << "函数模板被调用" << endl;
}
template <typename Type>
void func(Type a, Type b, Type c) {
cout << a + b + c << endl;
cout << "重载函数模板被调用" << endl;
}
void func(int a, int b) {
cout << a + b << endl;
cout << "普通函数被调用" << endl;
}
int main() {
//整型和字符型是可以自动发生隐式转换的
int a = 20;
int b = 30;
func(a, b); //调用普通函数
//通过空参数列表
func<>(a, b);
func(a, b, 100);
//更好的匹配举例
char ch = 'a';
char ar = 'd';
func(ch, ar); //普通函数的话还需要转换
return 0;
}
模板的局限性
#include <cstdbool>
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
Person(string name, int age) {
Name = name;
Age = age;
}
string Name;
int Age;
};
template <typename Type>
bool Cmp(Type& a, Type& b) {
if(a == b)
return true;
return false;
}
//解决办法
//利用具体化的版本实现,具体化优先调用
//相当于针对特殊的情况单独写,系统会自动调用
template <>
bool Cmp(Person& a, Person& b) {
if(a.Age == b.Age && a.Name == b.Name)
return true;
return false;
}
int main() {
int a = 10;
int b = 20;
if(Cmp(a, b))
cout << "a==b" << endl;
//对于自定义数据类型
Person p1("Tom", 10);
Person p2("Pony", 20);
// no match for 'operator==' (operand types are 'Person' and 'Person')
if(Cmp(p1, p2))
cout << "p1和p2是一样的" << endl;
return 0;
}
3) 类模板
基本语法
#include <cstdbool>
#include <iostream>
#include <string>
using namespace std;
template <class Nametype, class Agetype>
class Person {
public:
Person(Nametype name, Agetype age) {
Age = age;
Name = name;
}
void ShowInfo() {
cout << Name << " and " << Age << endl;
}
private:
Nametype Name;
Agetype Age;
};
//使用默认参数
template <class Nametype, class Agetype = int>
class Person1 {
public:
Person1(Nametype name, Agetype age) {
Age = age;
Name = name;
}
void ShowInfo() {
cout << Name << " and " << Age << endl;
}
private:
Nametype Name;
Agetype Age;
};
int main() {
//指定显示类型
Person<string, int> p1("张三", 18);
p1.ShowInfo();
// 1. 类模板没有自动类型推导的使用方式
// Person p2("李四",20);
// 2. 类模板在模板参数列表中可以有默认参数
Person1<string> p2("李四", 20);
p2.ShowInfo();
return 0;
}
类模板和函数模板的区别
#include <iostream>
#include <string>
using namespace std;
template <class Nametype, class Agetype>
class Person1 {
public:
Person1(Nametype name, Agetype age) {
Age = age;
Name = name;
}
void ShowInfo() {
cout << Name << " and" << Age << endl;
}
private:
Nametype Name;
Agetype Age;
};
template <class Nametype, class Agetype = int>
class Person2 {
public:
Person2(Nametype name, Agetype age) {
Age = age;
Name = name;
}
void ShowInfo() {
cout << Name << " and " << Age << endl;
}
private:
Nametype Name;
Agetype Age;
};
int main() {
Person1<string, int> p1("Zhangsan", 19);
p1.ShowInfo();
//类模板没有自动类型推导,必须指定
// Person p2("Lisi",20);
//类模板可以在参数列表中有默认参数
Person2<string> p2("Lisi", 30);
p2.ShowInfo();
return 0;
}
类模板中成员函数的创建时机
#include <iostream>
#include <string>
using namespace std;
class Person1 {
public:
void ShowPerson1() {
cout << "Person1 show" << endl;
}
};
class Person2 {
public:
void ShowPerson2() {
cout << "Person2 show" << endl;
}
};
template <class Type>
class MyClass {
public:
Type obj;
//类模板中的成员函数
void func1() {
obj.ShowPerson1();
}
void func2() {
obj.ShowPerson2();
}
};
int main() {
//通过这两个来说明类模板中的成员函数并不是一开始就创建的
// 而是调用的时候再创建
MyClass<Person1> m;
m.func1();
// m.func2();
MyClass<Person2> n;
// n.func1();
n.func2();
return 0;
}
类模板对象作为函数参数
#include <iostream>
#include <string>
using namespace std;
//先声明一个类模板
template <class T1, class T2>
class Person {
public:
Person(T1 name, T2 age) {
this->Name = name;
this->Age = age;
}
void ShowPerson() {
cout << "the name: " << this->Name << " and the age is " << this->Age << endl;
}
private:
T1 Name;
T2 Age;
};
// 传入方式
// 1. 指定传入类型、
void PrintPerson1(Person<string, int>& p) {
p.ShowPerson();
}
// 2.参数模板化
template <class T1, class T2>
void PrintPerson2(Person<T1, T2>& p) {
p.ShowPerson();
//其实我们也可在函数中输出程序推出的这个参数是什么类型
cout << "T1的类型是: " << typeid(T1).name() << endl;
cout << "T2的类型是: " << typeid(T2).name() << endl;
}
// 3. 整个类模板化
template <typename Type>
void PrintPerson3(Type& p) {
p.ShowPerson();
cout << "Type 的类型是: " << typeid(Type).name() << endl;
}
int main() {
Person<string, int> p("Zhangsan", 100);
PrintPerson1(p);
PrintPerson2(p);
PrintPerson3(p);
return 0;
}