1.地址传递与指针传递
student
{
string name;
int age;
int score;
}
值传递
void printstudent(student s)
{
cout <<"姓名"<<s.name<<"年龄"<<s.age<<"分数"<<score<<end1;
}
地址传递
void printstudent1(student *p)
{
cout <<"姓名"<<p->name<<“年龄”<<p->age<<“分数”<<p->score<<end1;
}
int main()
{
student s;
s.name = ''张三";
s.age = 20;
s.score = 85;
printstudent(s);
printstudent1(&s);
}
将函数的形参设置成指针,可以减少内存空间,而且不会复制新的副本。
student Arry[5]结构题数组。
2.C++中存储空间的分配(内存四区)
内存四区存在的意义:方便不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。
**程序运行前的区域
2.1 代码区
存放CPU机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份就可。
代码区是只读的,使其只读的原因是防止程序意外的修改指令。
2.2全局区
全局变量和静态变量存放在此(包括常量区、字符串常量、还有其他常量)。
该区域在程序结束以后由操作系统释放。
**程序运行后的
//全局变量
int c;
int d;//写在函数体外部的变量称为全局变量
//全局常量
const e;
int main(){
//局部变量
int a;//写在函数体里的都是局部变量
intb;
//静态变量
static int s_a = 10;
//字符串常量
'"hello word" 双引号框起来就是字符串常量。
//const常量
const int c_a = 10;//全局常量
}
代码的地址不在一个段里面。
****程序运行后
2.3栈区
由编译器自动分配释放,存放函数的参数值,局部变量等
注意:不要放回局部变量的地址,栈区开辟的数据由编译器自动释放。
2.4堆区
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
c++中主要利用new在堆区开辟内存
void* func()
{
//利用new关键字 可以将数据开辟到堆区 //如果不调用new,就在栈区,放回的地址就不可行
//指针的本质也是局部变量,放在栈上,指针保存的数据是放在堆区。
int *p = new int(10)
return p;
}
//看下图理解
3、new操作符
c++中利用new操作符在堆区开辟数据,由程序员分配,由程序员释放(delete)
int * func()
{
int * p = new int(10);
return p;
}
void test01()
{
int * p = func();
cout << *p <<end1;//
cout << *p <<end1; // 地址不会释放;
delet p;
cout << *p <<end1;//地址释放,报错。
}
****在堆区中开辟数组
void test02()
{
int * arr = new int[10];
for (int i =0 ; i<10 ; i++)
{
arr[i] = i+100;
cout<< arr[i]<<end1;
}
delete[] arr;
}
4.引用
引用:起别名。
int a =10;
int &b = a;
4.1 引用必须要初始化
一旦初始化后,就不可以更改了
4.2引用做函数参数时。
作用:函数传参时,可以利用引用的技术让形参修饰实参。
优点:可以简化指针修改实参
//交换函数
1、值传递
void mySwap01(int a , int b)
{
int temp = a;
a= b ;
b=a;
}
2、地址传递//利用形参修饰实参
void mySwap02(int *a , int *b)
{
int temp = *a;
*a= *b ;
*b=*a;
}
3、引用传递//形参修饰实参
void mySwap03(int &a , int &b)
{
int temp = a;
a= b ;
b=a;
}
int main()
{
int a = 10 ;
int b = 20;
mySwap01(a,b);
mySwap02(&a,&b);
mySwap03(a,b); }
4.3引用做函数的返回值
引用是可以作为函数的返回值的存在
1、函数不要返回局部变量的引用
2、函数的调用可以作为左值
int& test02()
{
static int a = 10;
return a;
}
int main()
{
int &ref2 = test02();
cout << "ref2"<<ref2 << end1;
test02() = 1000;如果函数的返回值是引用,这个函数调用可以作为左值
cout <<"ref2"<<ref2<<end1;
}
4.4引用的本质
本质:引用的本质是在c++内部实现的一个指针常量。
结论:c++推荐引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
4.5常量引用
作用:常量引用主要用来修饰形参,防止误操作。
在函数形参列表中,可以加const修饰形参,防止形参改变实参。
int main()
{
int a=10;
int &ref = 10;//不合法,
//加上const之后 编译器将代码修改 int temp = 10; int &ref = temp;
const int & ref = 10;//合法
}
void showValue(const int &a)
{
a =1000;//加入const,无法更改
cout << "val"<<val<<end1;
}
int main(){
int a = 100;
showValue(a);
}
5.函数的提高
5.1函数默认参数
int func (int a,int b =20 ,intc = 30)//如果我们传入数据,就用自己的数据,如果没有,就用默认值
{
return a+b+c;
}
int main()
{
cout << func(10)<<end1;
system("pause")
}
注意事项
1.如果某个位置已经有了默认参数,那么从这个位置以后,从左往右都必须有。
2.如果函数声明有了默认参数,函数实现就不能有默认参数。
int fun2( int a =10,int b=10);
int func2(int a = 20, int b =20)
{
return ...
}
5.2函数重载,
作用:函数名可以相同,提高复用性。
函数重载的条件:
#在同一作用域下
#函数名称相同
#函数参数类型不同,或者个数不同或者顺序不同
5.3函数重载注意事项
引用作为重载条件
函数重载碰到函数默认参数(写函数重载最好不要加默认参数)
6.类和对象
C++面向对象的三大特征为:封装、继承、多态
C++认为万事万物皆为对象,对象上有其属性和行为 称为类
6.1封装
6.1.1封装意义
封装的意义
#将属性和行为作为一个整体,表现生活中的事物
#将属性和行为加以权限控制
在设计类的时候,属性和行为写在一起,表现事物
//设计一个圆类,求圆的周长
//圆求周长的公式:
//class 代表设计一个类,类后面紧跟着的就是类的名称
class Cricle
{
//访问权限
public;
//属性
int m_r;
//行为
double calulateZC()
{
return 2 *PI *m_r;
}
}
int main(){
//通过圆类,创建具体的圆(对象)
Circle c1;
//给圆对象的属性进行赋值
c1.m_r = 10;
cout << "圆的周长为"<< c1.calulateZC()<<end1;
封装意义二:
类在设计时,可以把属性和行为放在不同的权限下,加以控制访问权限有三种:
1.public 公共权限 //成员类内可以访问 类外可以访问,
2.protected 保护权限//成员 类内可以访问 类外不可以访问 儿子可以访问父亲中的保护内容。
3.private 私有权限//成员 类内可以访问 类外不可以访问 儿子不可访问
class Person
{
public:
string m_Name;
protected:
string m_car;
private:
int m_Password;
public:
void func()
{
m_name = "张三';
m_Car = "拖拉机";
m_Password = "123456";
}
}
int main(){
Person p1;//类外不可访问
p1.m_Name = "李四";//类外可以访问
p1.m_Car = ''奔驰'';//类外不可访问}
6.1.2struc和class区别(其实差不多)
在C++中struct和class唯一的区别就在于默认的访问权限不同
区别:
#struct 默认权限为公共
#class默认权限为私有
6.1.3成员属性设置为私有
优点1 :将所有成员属性设置为私有,可以自己控制读写权限。
优点2:对于写权限,我们可以检测数据的有效性。
//设计人类
class Person
{
public:
设置姓名
void setName(string name)
{
m_Name = name;
}
读取姓名
string getName()
{
return m_Name;
}
int getAge()
{
}
private:
string m_Name;
int m_Age;
string m_Lover;
}
int main()
{
// Person m;
// m.m_Name = ://不行,不可访问私有属性
}
6.1.4拷贝构造函数调用时机
c++拷贝构造通常由三种情况
●1.使用一个已经创建完毕的对象来初始化一个新对象
●2.值传递的方式给函数参数传值
●3.以值方式返回局部对象
// 1.
class Person
{
public:
int age;
Person()
{
cout << "Person...析构.."<<end1;
}
Person(int age)//有参赋初始值
{
m_Age = age;
cout << "Person...有参构造函数调用.."<<end1;
}
Person(const Person &p)
{
m_Age = p.m_Age
cout << "Person...拷贝构造函数调用.."<<end1;
}
~Person()
{
cout << “Person构造”<<end1;
}
}
void test01()
{
Person p1(20);
Person p2(p1);
}
2.0
void doword(Person p)
{
}
void test02()
{
Person p;
dowork(p)
}
3.0
void doword2()
{
Person p1;
return p1;
}
void test03(){
Person p = dowork2();
}
int main(){
}
6.1.5构造函数调用规则
默认情况下,c++编译器至少添加三个函数
1.默认构造函数
2.默认析构函数
3.默认拷贝构造函数,对属性进行值拷贝
如果用户定义有惨构造函数,c++不会提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数,c++不会在提供其他构造函数
class Person
{
public:
int m_Age;
Person()
{
cout << "Person...析构.."<<end1;
}
Person(int age)//有参赋初始值
{
m_Age = age;
cout << "Person...有参构造函数调用.."<<end1;
}
Person(const Person &p)
{
m_Age = p.m_Age
cout << "Person...拷贝构造函数调用.."<<end1;
}
~Person()
{
cout << “Person构造”<<end1;
}
}
void test01()
{
person p;
p.m_Age = 18;
person p2((p);
}
6.1.6深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作(默认拷贝函数)
深拷贝:早堆区重新申请空间,进行拷贝操作
public:
int m_Age;
int *Height
Person(int age, int height)
{
m_Age =age;
m_Height = new int(height)
cout <<"Person有参"<<end1;
}
~Person()
{
//析构代码,将堆区开辟数据做释放操作
if(m_Height != NUll)
{
delete m_Height;
m_Height = Null;
}
}
6.1.7初始化列表
c++提供了初始化语法,用来初始化属性
class Person
[
Public:
//传统初始化
Person(int a, int b, int c)
{
m_A = a;
m_B= b;
m_C=c;
}
//初始化列表初始化属性
Person():m_A(10),m_B(20),m_C(30)
{
}
]
void test01()
{
Person p;//;初始化列表属性
Persom p(10,20,30);//传统初始化
}
6.1.7类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
//先有Phone 后有Person
析构的时候先析构Person,遵守栈区的概念,先进后出。
class Phone
{
public:
Phone (string pName)
{
m_Pname = pname ;
}
string m_Pname;
};
class Person
{
public:
Person(string name, string pName ): m_Name(name) ,m_Phone(pName)
{}
string m_Name;
Phone m_Phone;
};
void test01()
{
Person p("张三",“苹果”);
}
6.1.7静态成员
静态成员就是在成员函数前加上关键字static,称为静态成员
静态成员分为:
所有对象共享一份数据
在编程阶段分配内存
类内声明,类外初始化
静态成员函数
所有对象共享一个数据
静态成员函数只能访问静态成员变量
class Person
{
public:
static int m_A;
//静态成员变量也是有访问权限的。
private:
};
int Person :: m_A= 100;
void test1()
{
Person p;
Person p2
p2.m_A = 200;//数变为200
}
void test02()
(
静态成员变量,不属于某个对象上,所有对象都共享同一份数据
因此静态成员变量有两种访问方式
// Person p;
通过类名
cout << "m_A" << Person::m_A << end1;
)
6.1.8静态成员
静态成员就是在成员变量和成员函数前加上关键词static,称为静态成员
静态成员分为:
静态成员变量
所有对象共享同一份数据
在编译阶段分配内存
类内申明,类外初始化
静态成员函数
所有对象共享同一个函数
静态成员函数之恶能访问静态成员变量
class Person
{
public
static void func()
{
cout << "static void func调用" <<end1;
}
};
int Person::m_A=0;
void test01()
{
//通过对象来调用
Person p;
p.func();
通过类名访问
Person::func()
}
7.c++对象模型和this指针
7.1成员变量和成员函数分开存储
在c++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
c++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的空间
每个空对象也应该有独一无二的地址
class Person
{
int m_A;//内存4个 属于类的对象上
static int B//内存四个 不属于类的对象上
vod func()//静态成员函数 不属于类的对象上
};
void test01()
{
Person p;
cout<< "size of p "<< sizeof(p)<<end1;
}
7.2this指针概念
我们知道C++成员变量和成员函数是分开存储的
每一个非静态成员函数智慧诞生一份函数实例,也就是说多个同类型的对象会共同用一块代码
那么问题是:这一快代码是如何区分那个对象调用自己的呢?
c++是=通过提供特殊的对象指针,this指针,解决上述问题。
this 指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途:
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中放回对象本身,可使用return *this
class Person
{
public:
Person (int age)
{
this 指针指向被调用成员函数锁属对象
this->age =age;
}
int age;
Perison& PersonAddAge(Person &p)//如果不加&就一直返回的是一个新的值,而不是P2
{
this->age += p.age;
//this指向p2的指针,而*this指向的是就是p2这个对象本体
return *this;
}
}
1 解决名称冲突
void test01()
{
Person p1(18);
}
2.返回对象本身用*this
void test02()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
cout << "p2的年龄为 :"<<p2.age <<end1;
}
7.3 C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断代码的健壮性
class Person
{
public :
void showClassName()
{
}
void showName()
{
}
void showPersonage()
{
if(thtis == Null)
{
return;
}
cout <<"age=="<<this->m_Age<<end1;
}
}
void test01()
{
Person *p =NULL;
p->showPersonage;
}
7.4const修饰成员函数
常函数:
成员函数后加入const后我们称这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
声明对象前加const称该对象为常对象
常对象只能调用常函数
class Person
{
public:
this指针的本质,是指针常量
指针的指向是不可以修改的,若想要指针指向的值也不能改则
void showPerson()const//相当于const Person * const this;指针不可以改,数据也不可以改
{
m_A = 100;//每一个内部成员函数都有一个this指针来修饰相当于this->m_A =100
}
int m_A;
mutable int m-B;//加上关键字mutable在常函数中,也可以修改。
void test01()
{
Person p;
p.showPerson();
}
};
//常对象
void test02()
{
const Person p;
p.m_A = 100;
p.m_B=100;//在常对象下也可以修改
//常对象只能调用常函数
p.showPerson();
//p.func()//常对象,不可以调用普通成员函数,因为普通成员函数可以修改属性
}
8.友元
生活中你的家有客厅,有你的卧室
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许你的好闺蜜好基友进去
在程序李,有些私有属性 也想让类外特殊的一些函数和类进行范根,就需要到友元的技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字为friend
友元的三种实现
全局函数做友元
类做友元
成员函数做友元
class Building
{
//goodGay全局函数是Building好朋友,可以访问Building中私有成员
friend void goodGay(Building *building);
public:
Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
public:
string m_SittingRoom;
private:
string m_BedRoom;
}
//全局函数
void goodGay(Building *building)
{
cout <<"haojiyou 正在访问"<<building->m_SittingRoom << end1;
cout <<"haojiyou 正在访问"<<building->m_BedRoom << end1;
}
void test01()
{
Building building;
goodGay(&building);
}
int main()
{
test01;
}
//类做友元
class Building;
{
//GoodGay类是本类的好朋友,可以本类中私有的成员
friend class GoodGay;
public:
Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
Building:: Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
class GoodGay
{
Building *building;
void visit();//参数函数,访问Building中的属性
};
GoodGay::GoodGay()
{
building = new Building;
}
void GoodGay::visit()
{
cout << "好基友类正在访问:"<<building->m_SittingRoom<<end1;
cout << "好基友类正在访问:"<<building->BedRoom<<end1;
}
void test01()
{
GoodFay gg;
gg.visit();
}
int main()
{
test01;
}
//成员函数做友元
class Buildingl
{
//告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有内容
friend GoodGay::void visit();
public:
Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
class GoodGay
{
public:
GoodGay();
void visit();//让visit可以访问building中的私有成员
void visit2();不可以访问呢
private:
Building * building;
};
GoodGay::GoodGay()
{
building = new Building;
}
void GoodGay::visit();
{
cout << buildding->m_s_SittingRoom<<end1;
}
void GoodGay::visit2();
{
cout << buildding->m_BedRoom<<end1;
}
void test01()
{
GoodGay gg;
gg.visit();
}
9.运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
//加号运算符重载
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;
};
//全局函数的重载
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;
}
//Person + int类型
Person operator+(Person &p1,int num)
{
Person temp;
temp.m_A = p1.m_A + num;
temp.m_B= p1.m_B+num;
return temp;
}
void test()
{
Person p1;
p1.m_A=10;
p1.m_B =10;
Person p2 ;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;//
person p4= p1 +100;
}
9.1左移运算符重载
作用:可以输出自定义数据类型
class Person{
//不可以用成员函数重载左翼运算符。因为想让p在左边
void operator<<()
int m_A;
int m_B;
};
ostream operator<<(ostream &cout , Person p)//本质 operator <<(cout ,p) 简化cout<<p
{
cout<<"m_A="<<p.m_A<<m_B=p.m_B;
return cout;
}
void test01()
{
Person p;
p.m_A = 10;
p.m_B =10;
cout <<p<<end1;
}
9.2递增运算符重载
作用:通过重载递增运算符,实现自己的整型数据
class myinteger
{
friend ostream operator<<(ostream &cout , myinteger myint)//本质 operator <<(cout ,p) 简化cout<<p
public:
myinteger()
{
num = 0;
}
myinteger& operator++()
{
num++;
return *this;
};
myinteger operator++(int)//int代表占位参数,可以用于区分前置和后置递增(b++)//前置递增返回引用,后置递增返回值。
private:
int num;
};
//重载<<运算符
ostream operator<<(ostream &cout , myinteger myint)//本质 operator <<(cout ,p) 简化cout<<p
{
cout<<myint.num;
return cout;
}
void test1()
{
myinteger myint;
cout <<++myint<<end1;
}
9.3赋值运算符的重载//注意深拷贝浅拷贝问题
class Person
{
public:
Person(int age)
{
//将年龄数据开辟到堆区
m_Age = new int(age);
}
//重载赋值运算符
Person& operator=(Person &p)
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//编译器提供的代码是浅拷贝
//m_Age = p.m_Age;
//提供深拷贝 解决浅拷贝的问题
m_Age = new int(*p.m_Age);
//返回自身
return *this;
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
//年龄的指针
int *m_Age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1; //赋值操作
cout << "p1的年龄为:" << *p1.m_Age << endl;
cout << "p2的年龄为:" << *p2.m_Age << endl;
cout << "p3的年龄为:" << *p3.m_Age << endl;
}
int main() {
test01();
//int a = 10;
//int b = 20;
//int c = 30;
//c = b = a;
//cout << "a = " << a << endl;
//cout << "b = " << b << endl;
//cout << "c = " << c << endl;
system("pause");
return 0;
}
9.4重载关系运算符
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
};
bool operator==(Person & p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
else
{
return false;
}
}
bool operator!=(Person & p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return false;
}
else
{
return true;
}
}
string m_Name;
int m_Age;
};
void test01()
{
//int a = 0;
//int b = 0;
Person a("孙悟空", 18);
Person b("孙悟空", 18);
if (a == b)
{
cout << "a和b相等" << endl;
}
else
{
cout << "a和b不相等" << endl;
}
if (a != b)
{
cout << "a和b不相等" << endl;
}
else
{
cout << "a和b相等" << endl;
}
}
int main() {
test01();
system("pause");
return 0;
}
9.4函数调用运算符重载
* 函数调用运算符 () 也可以重载
* 由于重载后使用的方式非常像函数的调用,因此称为仿函数
* 仿函数没有固定写法,非常灵活