欢迎光临散文网 会员登陆 & 注册

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

2022-02-21 17:12 作者:行者无疆_hiter  | 我要投稿

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


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

全局变量 、字符串常量在常量区;

局部常量、局部变量地址很接近;


03 程序的内存模型-内存四区-栈区 P86 - 00:28





(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被编译器隐藏因此访问不到

黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难的评论 (共 条)

分享到微博请遵守国家法律