黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难

(1)程序运行前:代码区,全局区


全局变量、静态变量、全局常量、字符串常量地址很近,在全局区里;
全局变量 、字符串常量在常量区;
局部常量、局部变量地址很接近;

(2)程序运行后:栈区(存放局部变量、形参)、堆区
【1】栈:程序执行完,栈上数据内存被释放,再用指针访问属于非法操作,但是编译器会提供额外一次机会访问到,但之后无法访问。
不要返回局部变量地址,示例:

运行:

【2】

//在堆区开辟一个int的内存空间,放入10,返回其地址,赋值给指针变量p
返回p值1后,p值恒可以解引用访问10
程序员释放该内存前,一直可以调用

实际上:

new关键字:
在堆区开辟内存,堆区开辟的内存由程序员管理,手动开辟,手动释放,释放用delete
(1)
int* func()
{
int*p=new int(10);//返回指针
return p;
}
void test01()
{
int* p = func();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl;
delete p;//delete 该内存的指针
cout << *p << endl;//内存已经被释放,再次访问会报错,非法
}
int main()
{
test01();
}
(2)
void test02()
{
int* arr=new int[10];//[ ]里的数字指多少个连续空间,返回这些空间首地址
for (int i = 0; i < 10; i++)
{
arr[i] = i;
}
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
delete [ ] arr;//释放数组需要[ ],否则只能释放首地址对应都空间
}
int main()
{
test02();
}
注:类中的变量没有全局变量的说法,只有成员变量和静态成员变量的说法(用static声明)
类只是一种数据结构,只有类的实例才有意义
指针
32位操作系统,指针变量占4Bits


空指针:指针变量指向编号为0的空间的指针


指针需要先申请空间

常量指针
const int*p:指针指向能改,指向的值不能改
指针常量
int* const p:指针指向不能改,指向的值能改
指针和数组:数组名是数组首地址
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int*p=arr;
p++;//向后4个字节
结构体

//结构体类型
struct Student
{
String name;
int age;
int score;
};
//创建结构体变量
struct Student s1;
s1.name="贺泽宇“;
s1.age=20;
s1.score=200;
//创建结构体变量
struct Student s2={"贺泽宇“,20,200}
创建结构体类型时,struct不可以省略,创建结构体变量时struct可以省略
结构体数组

struct Student
{
String name;
int age;
int score;
};
struct Student arr[10 ]
结构体指针
struct Student
{
String name;
int age;
int score;
};
struct Student s={"张三",18,100};
struct Student* p=&s;
cout<<p->name;
结构体嵌套结构体
struct Student
{
String name;
int age;
int score;
};
struct Teacher
{
String name;
int age;
int id;
struct Student stu;
};
struct Teacher t;
t.stu.name="王新宇“;
结构体做函数参数
struct Student
{
String name;
int age;
int score;
};
//(1)值传递
void printStudent1(struct Student s)
{
cout<<s.name<<s.age<<s.score<<endl;
}
//(1)地址传递
void printStudent2(struct Student* p)
{
cout<<p->name<<p->age<<p->score<<endl;
}
int main()
{
struct Student s={"王新宇”,20,99};
printStudent1( s);
printStudent2( &s);
}
值传递的函数内部改变形参值,没有实际改变值;
地址传递的函数内部改变形参值,实际改变值;
地址传递传递指针,不会复制出一个新的副本,减少内存浪费。
结构体中const的使用
struct Student
{
String name;
int age;
int score;
};
void printStudent2(const struct Student* p)
//形参加上const使得其在函数内部无法修改
{
cout<<p->name<<p->age<<p->score<<endl;
}
int main()
{
struct Student s={"王新宇”,20,99};
printStudent2( &s);
}
引用:
int a=10;
int &b=a;
则:

int a=10;
int &b=a;
b=20;
cout<<a;//输出20
引用的注意事项
int &b;//错误语法,引用需要初始化
int a;
int &b=a;
int c=30;
b=c;//赋值
引用做函数参数
简化指针,用形参修改实参
void swap1(int*a,int*b)
{
int temp=*a;
*a=*b;
*b=temp;
}
void swap2(int&a,int&b)//此处原名和别名一样
{
int temp=a;
a=b;
b=temp;
}
引用做函数返回值
int& test1()//(1)不要返回局部变量的引用
{
int a=10;
return a;
}
int& test2()//(2)返回静态变量
{
static int a=10;
return a;
}
int main(
{
int&b=test1();//错误,因为a是局部变量,test1函数执行完后该变量释放,故而不正确
int&b=test1();//对
}
(3)函数调用返回值为引用时,可以作为左值
int& test3()
{
static int a=10;
return a;
}
int main()
{
int&ref=test3();
test3()=1000;
}
引用的本质

const

void showValue(const int& a)
{
cout<<a<<endl;
}
int main()
{
int a=100;
showValue( a);
}
函数默认参数

int f(int a,int b=20,int c=30)
{
return a+b+c;
}
int main()
{
cout<<f(10,30)<<endl;
}
输出:10+20+30
注意
(1)

(2)默认参数在声明里有,那么实现里不能有;
在实现里有,那么声明里不能有;

函数重载

作用域:全局、main()里
面向对象
封装
(意义1)将属性和行为作为一个整体表现一个事物
示例:设计一个圆类,求周长
const double PI=3.14;
class Circle
{
//访问权限
public:
//属性(通常是变量)
int m_r;
//行为(通常是用函数代表)
double calculate()
{
return 2*PI*m_r;
}
};
int main()
{
Circle c1;//实例化
c1.m_r=10;// "m_"为了用set函数赋值而加上的
cout<<"圆的周长“<<c1. calculate()<<endl;
}
(意义2)类在设计时,把属性和行为放在不同的权限下
成员:包括 成员变量+成员函数

class Person
{
public:
string m_Name;
protected:
string m_car;
private:
int m_password;
}
由上可知:类内函数可以访问所有权限的成员变量,与它自己权限无关

区别: 成员的访问权限
通常会将成员变量私有化
对象的初始化和清理
//这两个函数你写就用你的
1.构造函数(初始化): 给属性赋值

析构函数(清理):

class Person
{
public://构造函数、析构函数都需要
Person()
{
cout<<构造函数被调用<<endl;
}
}
int main()
{
Person p;
}
输出:构造函数被调用
class Person
{
public:
Person()
{
cout << "构造函数被调用" << endl;
}
~Person()
{
cout << "析勾函数被调用" << endl;
}
};
int main()
{
Person p;
// system("pause");//使得程序运行到该处中断
return 0;
}
输出:构造函数被调用
析构函数被调用

class Person
{
int age;
public:
Person()
{
cout << "构造函数被调用" << endl;
}//无参
无参构造函数是默认构造函数
Person(int a)
{
age=a;
cout << "构造函数被调用" << endl;
}//有参
Person(const Person &p)
{
age=p.age;
}//拷贝构造函数
~Person()
{
cout << "析勾函数被调用" << endl;
}//析构
};
int main()
{
Person p1;//调无参
Person p2(10);//调有参
Person p3(p1);
system("pause");
return 0;
}
对象的创建方法
(1)括号法
class Person
{
public:
int age;
Person()//无参构造函数
{
cout << "Person无参构造函数" << endl;
}
Person(int a)//有参构造函数
{
age = a;
cout << "Person有参构造函数" << endl;
}
Person(const Person& p)//拷贝构造函数
{
age = p.age;
cout << "拷贝构造函数调用" << endl;
}
~Person()
{
cout << "析构函数调用" << endl;
}
};
int main()
{
Person P1;
Person P2(10);
Person P3(P2);
cout << "P2的年龄" << P2.age << endl<< P3.age << endl;
system("pause");
return 0;
}
(2)显示法


(3)隐式法

总结:

拷贝构造函数的调用时机
①使用一个已经创建完毕的对象初始化一个新对象
②以值传递的方式给函数传值,拷贝一份给形参
③以值方式返回局部对象
class Person
{
public:
int age;
Person()
{
cout << "Person无参构造函数" << endl;
}
Person(int a)
{
cout << "Person有参构造函数" << endl;
age = a;
}
Person(const Person &p)
{
age = p.age;
cout << "Person拷贝构造函数" << endl;
}
~Person()
{
cout << "析构函数调用" << endl;
}
};
void test01()
{
Person p1(10);
Person p2(p1);//(1)使用一个已经创建完毕的对象初始化一个新对象
cout << p2.age<<endl;
}
void doWork2(Person p)
{
}
void test02()
{
Person p;
doWork2(p);//(2)值传递的方式给函数传值,拷贝一份给形参:Person p=p[隐含法]拷贝构造函数,这两个是不一样的,修改p,实际值不变
}
Person doWork3()
{
Person p1;
cout << (int*)&p1 << endl;
return p1;//以值方式返回局部对象
}
void test03()
{
Person p= doWork3();
cout << (int*)&p << endl;//(int*)&p 表示将地址强制转换成指向int 类型数据的指针。
}
int main()
{
test03();
system("pause");
return 0;
}
输出:值的方式返回局部对象,会先拷贝出一个属性相同的替身,返回替身,将原局部对象销毁。
下图验证了返回值的地址和局部对象地址不同,即:是替身。

一般情况下,编译器默认给任意一个类添加至少三个函数

注:
若用户定义有参构造函数,C++不再提供无参构造函数,只提供拷贝构造函数;
若用户定义有参构造函数,C++不再提供任何函数
class Person
{
public:
int age;
Person(const Person &p)
{
age = p.age;
cout << "Person拷贝构造函数" << endl;
}
~Person()
{
cout << "析构函数调用" << endl;
}
};
void test02()
{
Person p1;
Person p2(18);
}
int main()
{
test02();
system("pause");
return 0;
}

浅拷贝:赋值拷贝
深拷贝:在堆区重新申请空间,进行拷贝操作
①浅拷贝版本,利用编译器的拷贝构造函数
浅拷贝程序报错的原因:堆区内存被释放了第二次
class Person
{
public:
int age;
int* height;
Person()
{
cout << "Person无参构造函数" << endl;
}
Person(int a,int h)
{
age = a;
height =new int(h);
cout << "Person有参构造函数" << endl;
}
~Person()
{
//析构函数,将堆区开辟的数据释放
if (height != NULL)//即未被释放
{
delete height;
height = NULL;//防止野指针出现,置空
}
cout << "析构函数调用" << endl;
}
};
void test01()
{
Person p1(18,160);
cout << "p1的年龄为:" << p1.age<< " 身高为:" <<*p1.height<< endl;
Person p2(p1);
cout << "p2的年龄为:" << p2.age << " 身高为:" << *p2.height << endl;
//系统栈先进后出,故p2先被释放,先执行析构函数,p1后被释放,后执行析构函数
}
int main()
{
test01();
system("pause");
return 0;
}
注:编译器写的拷贝构造函数
Person(const Person& p)
{
age = p.age;
height = p.height;//正是这行代码导致重复释放堆区内存,导致非法
}
如图:堆区内存先被p2释放,再被p1释放

可以让p2的指针指向另一个堆内存,该内存存放的仍是p1指针所指向的值
即:

②
class Person
{
public:
int age;
int* height;
Person()
{
cout << "Person无参构造函数" << endl;
}
Person(int a,int h)
{
age = a;
height =new int(h);
cout << "Person有参构造函数" << endl;
}
Person(const Person& p)//自己写拷贝构造函数
{
cout << "Person拷贝构造函数" << endl;
age = p.age;
height = new int( * p.height);//重新在堆区开辟空间
}
~Person()
{
if (height != NULL)
{
delete height;
height = NULL;
}
cout << "析构函数调用" << endl;
}
};
void test01()
{
Person p1(18,160);
cout << "p1的年龄为:" << p1.age<< " 身高为:" <<*p1.height<< endl;
Person p2(p1);
cout << "p2的年龄为:" << p2.age << " 身高为:" << *p2.height << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
注:平常不写析构函数,只有在堆区有内存没有释放干净的时候才写,释放内存,并置空指针;
析构函数在对象释放时会被自动调用,实际上是:
p1.~Person( ) ;
总结:若属性有在堆区开辟的,一定要自己提供深拷贝构造函数,防止浅拷贝带来的问题
初始化列表
除了构造函数外另一种初始化属性的语法
class Person
{
public:
int A, B, C;
Person(int a,int b,int c) :A(a), B(b), C(c)
{
}
};
void test01()
{
Person p(10,20,40);
cout << "A:" << p.A << " B:" << p.B << " C:" << p.C << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
类A对象做类B的成员变量
class Phone
{
public:
string pname;
Phone(string pn)
{
pname = pn;
cout << "Phone的构造函数调用" << endl;
}
};
class Person
{
public:
string Name;
Phone P;
//即Phone P =p;[隐式法],即:Phone P(p);
Person(string name, string p) :Name(name),P(p)
{
cout << "Person的构造函数调用"<<endl;
}//Person类成员变量P是Phone类的对象,调用Person类构造函数对Person类对象初始化时,先用了隐式法调用了Phone类构造函数。①
};
void test01()
{
Person p("王新宇", "华为");
cout << p.Name << "拿着:" << p.P.pname << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
验证①:

与实际工程基本原理【先构造局部,再构造整体】一样,当其他类的对象做一个类的成员时,先构造其他类的对象,再构造自身的对象。
析构和构造顺序相反:
class Phone
{
public:
string pname;
Phone(string pn)
{
pname = pn;
}
~Phone()
{
cout << "Phone的析构函数调用" << endl;
}
};
class Person
{
public:
string Name;
Phone P;
Person(string name, string p) :Name(name),P(p)
{
}
~Person()
{
cout << "Person的析构函数调用" << endl;
}
};
void test01()
{
Person p("王新宇", "华为");
cout << p.Name << "拿着:" << p.P.pname << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
静态成员函数
在成员函数前加static,所有对象共享一个函数,只能访问静态成员变量
class Person
{
public:
static void func()
{
cout << "static void func()被调用" << endl;
}
};
void test01()
{
/*Person p;
p.func();*/
Person::func();//因为静态,所有对象共享该函数,所有不需要创建对象,用类名可以访问
}
int main()
{
test01();
system("pause");
return 0;
}
class Person
{
public:
static int A;
int B;
static void func1()
{
A = 100;
B = 200;//报错,非静态成员变量是属于某个具体对象的,所以它不应被静态成员函数调用
cout << "static void func()被调用" << endl;
}
private:
static void func2()
{
cout << "static void func2()被调用" << endl;
}
};
int Person::A = 0;//静态成员变量需要类内声明,类外初始化
void test01()
{
/*Person p;
p.func();*/
Person::func1();
Person::func2();//报错,私有的静态成员函数不可在类外调用
}
int main()
{
test01();
system("pause");
return 0;
}
总结:静态成员函数可以创建对象调用,也可以直接类调用。
成员变量和成员函数分开储存
class Person
{
};
void test01()
{
Person p;
cout << "size of p:" << sizeof(p) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
输出:1
空对象占用字节为1:为了区别不同空对象,空对象应占用内存,C++编译器分配1个字节
class Person
{
int A;//默认都是非静态的
static int B;
void Func() {
}
static void Func2() {
}
};
int Person::B = 0;
void test02()
{
Person p;
cout << "size of p:" << sizeof(p) << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
只有静态成员变量属于类的对象

解释之前的习惯:
class Person
{
public:
int m_age;//成员前面加m_,区别形参和成员
Person(int age)
{
m_age = age;
}
int age;
Person(int age)
{
this->age = age;//this指针指向调用该非静态成员函数的对象
}
};
void test02()
{
Person p(20);
cout << "年龄是:" << p.m_age << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
this指针
每一个非静态成员函数被许多对象共有,所以该函数用this指针区分不同的对象;
this指针指向调用该非静态成员函数的对象;
每一个非静态成员函数都隐含一个this指针,不需要定义,直接使用。
用途:当形参和成员变量同名时,用this区分;
当在类的非静态成员函数内访问对象本身时使用*this.
class Person
{
public:
int age;
Person(int age)
{
this->age = age;
}
Person& PersonAddAge(Person& p)
{
this->age += p.age;
return *this;
}//这里返回值类型是引用类型,是为了配合之后的链式编程
};
void test02()
{
Person p1(10);
Person p2(10);
p1.PersonAddAge(p2).PersonAddAge(p2).PersonAddAge(p2).PersonAddAge(p2);//链式编程
cout << "年龄之和是:" << p1.age << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
若返回值不是引用类型,再分析这串代码:
p1.PersonAddAge(p2).PersonAddAge(p2);
值方式返回对象时,返回的是拷贝出来的”替身“,此后加上的一串.PersonAddAge(p2),均是替身、替身的替身.......
最终输出的仍是p1的属性,故而没能输出我们想要得到的结果。
以值类型返回对象时,返回的是创建的新的对象;
以引用类型返回对象时,不会创建新对象,会一直返回p1
空指针调用成员函数

class Person
{
public:
int m_age;
void ShowClass()
{
cout << "this class is Person" << endl;
}
void ShowAge()
{
cout << "age is:" << m_age<<endl;//m_age是成员变量,其前面默认存在this->,
即this->m_age,因为this就是p, 就是NULL,空的对象不可能有成员函数
}
};
void test01()
{
Person* p = NULL;
p->ShowClass();//正常执行
p->ShowAge();//报错
}
int main()
{
test01();
system("pause");
return 0;
}
修改后,提高健壮性:
class Person
{
public:
int m_age;
void ShowClass()
{
cout << "this class is Person" << endl;
}
void ShowAge()
{
if (this == NULL)
{
return;
}
cout << "age is:" << m_age<<endl;
}
};
void test01()
{
Person* p = NULL;
p->ShowClass();
p->ShowAge();
}
int main()
{
test01();
system("pause");
return 0;
}
总结:空指针可以访问成员,有this的需要加上一些提高健壮性的代码
const修饰成员函数

常函数内不可修改成员变量,成员变量声明时加mutable则可以在常函数内修改

常对象只能调用常函数,常对象的属性不允许修改(除了mutable修饰过的)

class Person
{
public:
int m_a;
mutable int m_b;
//成员函数里的成员变量前有this->,而this的本质是 Person*const this;
this=&p;
void showPerson()const
//加上const,本质上是改变了this的定义为const Person*const this;
指向的值也不能修改了
{
m_a = 10;//报错
m_b = 20;
}
};
void test01()
{
const Person p;//常对象
p.m_a = 100;
p.m_b = 2;
cout << "p.m_a=" << p.m_a << "p.m_b=" << p.m_b;
}
int main()
{
test01();
system("pause");
return 0;
}
友元技术
全局函数做友元
private属性在类外访问不到(可以被public下成员函数调用)
class Buiding
{
// GoodGay全局函数是 Buiding类好朋友,可以访问 Buiding里私有属性
friend void GoodGay(Buiding* buiding);
public:
Buiding()
{
m_BedRoom = "卧室";
m_SittingRoom = "客厅";
}
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
//全局函数
void GoodGay(Buiding* buiding)
{
cout << "好基友正在访问:" << buiding->m_SittingRoom<<endl;
cout << "好基友正在访问:" << buiding->m_BedRoom << endl;
}
void test01()
{
Buiding buiding;
GoodGay(&buiding);
}
int main()
{
test01();
system("pause");
return 0;
}
类做友元
class Building
{
//GoodGay是本类好朋友可以访问属性
friend class GoodGay;
public:
Building();
string m_SittingRoom;
private:
string m_BedRoom;
};
//类外写成员函数
Building ::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
class GoodGay
{
public:
GoodGay();
void visit();//参观函数,访问 Buiding里的所有属性
Building* building;
};
GoodGay::GoodGay()
{
building = new Building;
}
void GoodGay::visit()
{
cout << "好基友正在访问:" << building->m_SittingRoom << endl;
cout << "好基友正在访问:" << building->m_BedRoom << endl;
}
void test01()
{
GoodGay gg;
gg.visit();
}
int main()
{
test01();
system("pause");
return 0;
}
成员函数做友元
class Building
{
friend void GoodGay::visit();
public:
Building();
string m_SittingRoom;
private:
string m_BedRoom;
};
Building::Building()
{
m_BedRoom = "卧室";
m_SittingRoom = "客厅";
}
class GoodGay
{
public:GoodGay();
Building* building;
void visit();
void visit2();
};
GoodGay::GoodGay()
{
building = new Building;
}
void GoodGay::visit()
{
cout << "visit函数正在访问:" << building->m_SittingRoom;
}
void GoodGay::visit2()
{
}
void test01()
{
GoodGay gg;
gg.visit();
}
int main()
{
test01();
system("pause");
return 0;
}

class Person
{
public:
//成员函数完成重载
Person operator+(Person& p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
int m_A;
int m_B;
/}//此时是将p3=p1.operator+(p2)简化成
p3=p1+p2
////全局函数完成重载
//Person operator+ (Person & p1,Person&p2)
//{
// Person temp;
// temp.m_A = p1.m_A + p2.m_A;
// temp.m_B = p1.m_B + p2.m_B;
// return temp;
//}//此时是将p3=operator+(p1,p2)简化成
p3=p1+p2
void test01()
{
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 20;
p2.m_B = 20;
Person p3 = p1 + p2;
cout << "p3.m_A: " << p3.m_A <<endl<< "p3.m_B: " << p3.m_B << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
左移运算符(<<)重载

目的:用cout<<p; 将p的所有属性打印
class Person
{
public:
friend void test01();
friend ostream& operator<<(ostream& out, Person& p);
private:
int m_A;
int m_B;
};
ostream& operator<<(ostream& out, Person& p)
{
cout << "m_A=" << p.m_A << "m_B=" << p.m_B;
return out;//这里用out/cout或者其它都行
}//cout为ostream类的对象
void test01()
{
Person p;
p.m_A = 10;
p.m_B = 20;
cout << p << p <<"hello world"<< p << endl;//链式编程,所以返回值是类的引用类型
}
int main()
{
test01();
system("pause");
return 0;
}
总结:运算符重载配合友元,用来输出自定义数据类型
递增运算符(++/--)重载
//自定义整型
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
//重载前置++运算符
MyInteger& operator++()//此处返回值是引用类型,为了++(++myint)
{
m_Num++;
return *this;
}
//重载后置++运算符
MyInteger operator++(int)//传入int作为占位参数
//这里返回值不是引用,因为返回局部变量,返回引用非法
{
MyInteger temp = *this;
m_Num++;
return temp;
}
MyInteger()
{//默认初始值是0
m_Num = 0;
}
private:
int m_Num;
};
ostream& operator<<(ostream& cout, MyInteger myint)
{
cout << myint.m_Num<<endl;
return cout;
}
void test01()
{
MyInteger myint;
cout <<++(++myint )<< endl;
cout << myint;
}
void test02()
{
MyInteger myint;
cout << (myint++)++ << endl;
cout << myint << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
赋值运算符(=)的重载
应用场景:对象p1,p2,p1=p2默认是浅拷贝操作
如果该对象的一些属性创建在堆区,则导致重复释放堆内存。如果还想继续使用p1=p2,不非法的话,需要在类里重载operator=()
此外若想实现连等,operator=()需要返回自身
①
class Person
{
public:
int* m_Age;
Person(int age)
{
m_Age = new int(age);
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
};
void test02()
{
Person p1(18);
Person p2(20);
cout <<"p1的年龄是:" <<* p1.m_Age << " p2的年龄是:" << *p2.m_Age << endl;
p2 = p1;//所谓的"浅拷贝"
cout << "p1的年龄是:" << *p1.m_Age << " p2的年龄是:" << *p2.m_Age << endl;
}//执行过后,调用析构函数,重复释放堆内存,非法
int main()
{
test02();
system("pause");
return 0;
}
②
class Person
{
public:
int* m_Age;
Person(int age)
{
m_Age = new int(age);
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
void operator=(Person&p)
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}//先判断是不是空,不是空的则先释放再置空
m_Age = new int(*p.m_Age);//深拷贝
}
};
void test02()
{
Person p1(18);
Person p2(20);
cout <<"p1的年龄是:" <<* p1.m_Age << " p2的年龄是:" << *p2.m_Age << endl;
p2 = p1;
cout << "p1的年龄是:" << *p1.m_Age << " p2的年龄是:" << *p2.m_Age << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
③实现 p3=p2=p1
class Person
{
public:
int* m_Age;
Person(int age)
{
m_Age = new int(age);
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
Person& operator=(Person& p)//返回值是引用类型才是真正的返回本身
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//深拷贝
m_Age = new int(*p.m_Age);
return *this;
}
};
void test02()
{
Person p1(18);
Person p2(20);
Person p3(23);
cout << "p1的年龄是:" << *p1.m_Age << " p2的年龄是:" << *p2.m_Age << endl;
p2=p3 = p1;
cout << "p1的年龄是:" << *p1.m_Age << endl << " p2的年龄是:" << *p2.m_Age << endl << " p2的年龄是:" << *p3.m_Age << endl;
}
int main()
{
test02();
system("pause");
return 0;
}
关系运算符重载
中规中矩()
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age)
{
m_Age = age;
m_Name = name;
}
bool operator==(Person& p)
{
if (m_Age == p.m_Age && m_Name == p.m_Name)
{
return 1;
}
else return 0;
}
};
void test02()
{
Person p1("TOM", 18);
Person p2("TOM", 18);
if (p1 == p2)
{
cout << "p1,p2相等" << endl;
}
else
{
cout << "p1,p2不相等" << endl;
}
}
int main()
{
test02();
system("pause");
return 0;
}
函数调用符()重载
(仿函数)
class FakeFunc
{
public:
int operator()(int a1, int a2)
{
return a1 + a2;
}
void operator()(string test)
{
cout << test << endl;
}
};
void test02()
{
FakeFunc f1;
FakeFunc f2;
f1("rnm");
cout<<f2(100, 100)<<endl;//f1,f2为对象,以对象(参数)的格式调用()的重载
cout << FakeFunc()(100, 100) << endl;//匿名对象在该行代码运行后销毁,【类名+()代替一个对象】
}
int main()
{
test02();
system("pause");
return 0;
}
继承

继承方式:
public: 父类属性为public/protected的,子类中依然public/protected.
protected:父类属性为public/protected的,子类中均protected.
private:父类属性为public/protected的,子类中均private

protected和private权限区别:
protected:父类内部访问,子类内部也可以访问,父类、子类类外不能访问。
private:父类内部访问,类外不能访问。
class Base1
{
public:
int m_A=100;
protected:
int m_B;
private:
int m_C;
};
class Son1 :protected Base1
{
public:
void func()
{
m_A = 100;
m_B = 100;
m_C = 100;//报错
}
};
void test01()
{
Son1 s1;
Base1 b1;
cout<<s1.m_A;//报错
}
int main()
{
test01();
system("pause");
return 0;
}
注:父类里所有非静态成员属性均被子类继承,但private被编译器隐藏因此访问不到