头文件不同
c++采用iostream库,并且作用与std领域中;
标准格式:
Using namespace std
#include<iostream>
Int main()
{
xxxxx
System("pause");
Return 0;
}
提出了四个作用区域
全局区 代码区 栈区 堆取
全局区:
用来存放所有的全局变量,或宏定义代码,静态变量和常量;
代码区:
用来存储所有的代码;
栈区:
由编译器自动分配,用于存储局部变量和函数值等:
//再利用函数时不能让其返回值为其中的局部变量;
因为当函数执行完后,该内存被释放;无法找到该变量内存;
堆区:
由程序员分配和释放的区
引入新的变量类型
Bool string new
布尔型变量 标志真假 真表示1 假表示0
字符串类型变量 string str;直接定义一个类型变量;
New 动态申请内存 new int(10);申请一个存10的int型空间内存,并返回一个地址;new int[10]申请10个连续空间的数组;当要释放该内存时,利用delete p;若为一段连续的数组空间为 delet []p;
输出保留小数点方式
#include <iomanip>
- //两位小数
- //第一种写法
- cout<<setiosflags(ios::fixed)<<setprecision(2);
- //第二种写法
- cout.setf(ios::fixed);
- cout<<setprecision(2);
- //第三种写法
- cout<<fixed<<setprecision(2);
setprecision(n)
功能:控制浮点数显示的有效数字个数。
fixed
setprecision(n)和fixed合用的话可以控制小数点后有几位
cout<<setiosflags(ios::fixed);
cout.setf(ios::fixed);
cout<<fixed;
提出了引用这一用法
给一个变量取别名,并且该别名与原名起同等作用,
|
再利用引用作为函数返回值时,是不被允许的,由于函数中的局部变量作用在栈上,执行完后该内存被释放,不能返回主函数中;
例如:
Int&fac()
{
Static Int a=10;
Return a; //此时a相当于a的别名,即别名与原名可相同
}
Void main ()
{
Int &p=fac();//此时由于编译器的保存机制,第一次输出时会保留a的值,第二次输出则为乱码,此时p为a的别名,即p为指向a的指针常量,
p=100;//此时相当于a=100;
Fac()=1000;//fac函数返回的是a的别名,即a=1000,那么对应的别名p=1000
}
若我们将作用于栈上的局部变量转化为作用在全局上的静态变量时,此时a值可返回,因为内存不被释放,即在前加static;
若将引用作为函数参数,
即
#include<iostream>
#include<string>
using namespace std;
void fac(int&a,int&b){
a=10;
b=100;
return;
}
int main(){
int a,b;
fac(a,b);
cout<<a;
cout<<b;
return 0;
}
此时相当于对传入的实参取了两个别名a,b;对别名a与b的操作同样对原名实参有相同作用;与指针传参相同。传参方式可以分为三类,直接传参,指针传参,引用传参;
若我们不想让某个传入函数中的值发生变化,可以在其前加上const;之后我们就不能对其操作了;
goto语句
跳转语句
格式:
Goto +标识符
在需要跳转的地方加上:
标识符:
类与对象
利用class或struct封装 类别
一个类别包含属性 行为 访问限制
其中属性是共有的特征即为声明的变量类型;行为是对属性进行操作的函数;
访问限制包含public private protected 其中这三种都可以在类中进行访问和操作;
但是private protected 只能作用在类中,即只能在class中;我们可以将属性放在private中,然后利用public中的函数调用来实现对private中的变量的赋值和读取;
封装好类后;我们可以在主函数中利用类来实例化一个对象,在对对象的属性进行操作;
匿名对象
不用写名字:
Person ();//匿名对象 使用后立即释放
类中调用其他类别:
例如:
#include<iostream>
#include<cmath>
using namespace std;
//封装点类
class point{
private:
double x,y;
public:
//获取 读出x,y
void set_x(double x1){
x=x1;
}
void set_y(double y1){
y=y1;
}
double gets_x(){
return x;
}
double gets_y(){
return y;
}
};
//封装圆类
class circule{
private:
double rr;
point center; //调用其他类
public:
//获取 读半径
void set_r(double r){
rr= r;
}
double gets_rr(){
return rr;
}
//获取圆心 设置圆心
void set_center(point center1){
center=center1;
}
point gets_center(){
return center;
}
};
//判断点和圆关系
void result(circule c1,point p1){
//计算两点距离
double distance;
distance= pow(c1.gets_center().gets_x()-p1.gets_x(),2)+pow(c1.gets_center().gets_y()-p1.gets_y(),2);
if(pow(c1.gets_rr(),2)==distance)
cout<<"点在圆上"<<endl;
else if (c1.gets_rr()*c1.gets_rr()<distance)
cout<<"点在圆外"<<endl;
else cout<<"点在圆内"<<endl;
}
int main(){
point p1,p2; //p1表示待测点 p2便是圆心
circule c1; //圆
//设置参数
c1.set_r(10);
p1.set_x(10);
p1.set_y(10);
p2.set_x(10);
p2.set_y(0);
c1.set_center(p2);
result(c1,p1);
system("pause");
return 0;
}
即一个类可以调用另一个类,从而通过该类来设置其中的其他属性;例如圆类中调用了点类,来实现定义圆心,通过访问点类中的其他元素实现对圆心的设置;其次;类可以作为函数的返回值,和形参;这一点与struct相似;但是struct默认作用域为public,class默认为private;
构造函数和析构函数
在初始化对象时,需要写一个构造函数(必须要有的,若自己不写,编译器会帮你写,并且为一个空函数),当对象需要被清理时(对象被回收时),调用析构函数(若不写编译器会帮你写,并且为空函数)
具体语法如下:
构造函数:类型(){
}
析构函数:~类型(){
}
其中构造函数可以分为有参数和为无参数类型;即无参构造和有参构造
按照类型分可以分为普通构造和拷贝构造
拷贝函数调用时机
1.使用一个以及创建好的对象来初始化一个对象
例如:
Person p1 (10);//有参构造
Person p2 (p1);//拷贝构造//此时p2与p1一样;相当于拷贝了一个相同的对象
2.值传递的方式给函数参数传值
void work1(person p){
//此时对形参的改变不影响实参,相当于给实参p拷贝了一个副本传入函数中;
调用了拷贝函数;
}
3.以值方式返回局部变量
例如:
在一个函数类型为person类中,申请了一个局部对象p1,当返回p1时,编译器会自动调用拷贝函数,拷贝一个p1的副本作为返回值传回,此时p1已经被释放;
Person work1(){
Person p1;
Return p1;
}
如何调用 拷贝构造 有参数构造
- 小括号法
例如定义了一个 person类别
在调用其中 的 有参 拷贝 函数时:
Person p1 (10);//有参构造
Person p2 (p1);//拷贝构造
- 显示法:
Person p1=person(1);//调用有参构造 //相当于给匿名对象取了个名字p1 和p2
Person p2 =person(p1);//调用拷贝构造;
- 隐式转换法:
Person p1=1;//相当于写了person p1=person(1); 调用有参构造
Person p2=p1;//拷贝构造;
普通构造和拷贝构造语法区别:
拷贝构造相当于复制了一份对象,将该对象与原对象属性进行复制,且传入的对象不能改变;并且以引用的方式传入;
类型(const 类型 &p ){}
在定义一个类时至少要有三个函数,分别是构造函数 析构函数 拷贝函数;若不写 系统自动初始化这些函数类型
若已经写了有参构造函数,系统不会提供无参函数,会提供拷贝函数
若写了拷贝函数,系统将不会提供任何函数,需要自己实现
深拷贝与浅拷贝
浅拷贝为编译器为我们自己实现的拷贝构造函数,主要实现对传进的对象进行复制各个属性;
深拷贝为我们自己对拷贝构造函数进行改变,让他在堆区重新创建一个内存空间用于存储对象的属性;
在堆区开辟的内存空间需要在析构函数中释放操作;
主要问题:
当我们在堆区开辟一个内存存储属性时,若利用编译器为我们提供的浅拷贝,当我们申请了p1对象,并且通过p1拷贝了出了p2时,当我们要释放在堆区开放的内存时,在析构函数中释放堆区内存,在释放p1和p2两个对象中的堆区内存时,会造成重复释放问题,导致系统崩溃;因此我们需要采用深拷贝,让拷贝构造函数自己在堆区开辟内存,存放属性,释放内存时,各自释放各自的,不会发生重复释放问题;
具体代码:
Class solution{
//有参构造函数
Solution(int age,int height)
{
m_height=New int(height);//在堆区申请一块存放height内存,返回地址
m_age=age;
}
~Solution()//析构函数
{
//释放堆区的内存
If(m_height!=null)
Delet m_height;
m_height=null;
}
//深拷贝构造函数
Solution(const solution &p)
{
m_age=p.m_age;
m_height=New int(*p.m_height);//重新申请一块新的存放相同数据的堆区内存
//m_height=p.m_height//浅拷贝默认提供的直接复制
}
Public:
Int m_age;//对象属性
Int *m_height;//用于存放堆区申请内存的地址
};
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
初始化对象
通过构造函数来实现对属性的初始化,即赋值操作:
传统的方式是构造有参构造函数,让其传入参数,在函数体中再去给属性赋值;
还有一种新方式,直接再构造函数小括号后面()加上:和属性对应的初值;最后跟上{}
例如:(已经定义了两个对象前提)(该对象为private类型 只能在类中访问)
//传统意义:
Person (int a,int b)
{
Age=a;
Height=b;
}
//初始化对象
Person ():age(10),height(180)
{
}
或是
Person (int a , int b):age(a),height(b)
{
}
在主函数中定义一个对象时:
Person p1(10 ,180)//第二种方式直接赋值初始化
Person p1;//第一种方式 已经初始化值了
类对象作为类成员
在一个类中成员为其他类对象时我们称该成员为对象成员:
当其他对象作为本类成员,构造函数先构造类对象,在构造自己;
析构的顺序与构造相反;类对象后析构;
例如:
#include<iostream>
using namespace std;
class phone{
public:
//构造函数
phone(string phone){
m_phone=phone;
cout<<"Phone的构造函数"<<endl;
}
//析构函数
~phone(){
cout<<"phone的析构函数"<<endl;
}
public:
string m_phone;
};
class people{
public:
//构造函数
people(string name,string phonename):m_name(name),my_phone(phonename)
{
cout<<"people 的构造函数"<<endl;
}
//析构函数
~people(){
cout<<"people的析构函数"<<endl;
}
public:
string m_name;
phone my_phone;
};
void test01(){
people p1("yupuning","iphone 11");
cout<<p1.m_name<<"的"<<p1.my_phone.m_phone<<endl;
}
int main(){
test01();
system("pause");
return 0;
}
静态成员//位于全局作用域上 不属于类对象
静态成员对象
1.所有对象共享一份数据;//数据值相同,
2.编译阶段分配内存;
3.类内声明;类外初始化(在括号外给静态成员对象赋值)
静态变量不属于任何一个对象,所有对象都共享同一份数据;因此静态成员有两种访问方式:
1.通过对象访问
2.通过类名访问
静态成员变量也是有访问权限的,若为private,则内外无法访问;
#include<iostream>
using namespace std;
class people{
public:
//构造函数
people()
{
cout<<"people 的构造函数"<<endl;
}
//析构函数
~people(){
cout<<"people的析构函数"<<endl;
}
public:
static string m_name;
string my_phone;
};
string people::m_name="YUpuning";//类外初始化;
void test01(){
//通过对象访问
people p1;
cout<<p1.m_name<<endl;
//通过类名访问
cout<<people::m_name<<endl;
}
void test02(){
people p1;
cout<<p1.m_name<<endl;
p1.m_name="yupuning";
people p2;//p1改变了静态成员,p2的也跟着改变
cout<<p2.m_name<<endl;
}
静态成员函数
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
静态函数不属于任何一个对象,所有对象都共享同一份数据;因此静态函数有两种访问方式:
1.通过对象访问
2.通过类名访问
静态函数可以访问静态成员变量,由于静态变量是共享的;不能访问非静态成员变量;无法区分到底是那个对象的成员;
静态成员函数也是有访问权限的;
class people{
public:
//构造函数
people()
{
cout<<"people 的构造函数"<<endl;
}
//静态函数
static void func(string name){
m_name=name;
}
//析构函数
~people(){
cout<<"people的析构函数"<<endl;
}
public:
static string m_name;
string my_phone;
};
成员变量 和成员函数 分开存储
空对象占用内存为1;c++编译器会给每个空对象分配一个字节空间,是为了区分空对象占内存位置;意思就是给该空对象给了一个地址,这样就可以对其处理和找到;
非静态成员函数不属于类对象上;
静态成员变量不属于类对象上;
静态函数不属于类对象上;
只有非静态成员变量属于对象上;
This 指针:this指针指向被调用的成员函数所属对象
this指针的本质为指针常量 指向是不能改变的 但是指向的对象的内容可以改变
1.解决名称冲突(形参和成员名重名)
class people{
public:
//构造函数
people(string my_phone)
{
This->my_phone=my_phone; //this指向该属性的对象this->表示对象中成员
cout<<"people 的构造函数"<<endl;
}
//静态函数
static void func(string name){
m_name=name;
}
//析构函数
~people(){
cout<<"people的析i构函数"<<endl;
}
public:
static string m_name;
string my_phone;
};
2.返回对象本身;*this;
Person &personaddage(person& p) //以引用的方式传入其本体,若不用引入,则会拷贝一个新的函数返回
This->age+=p.age;//this指向调用该函数的对象
Return *this;//返回调用该函数的对象本体
}
链式编程思想:无限调用函数;
Person p2(10);
P2.personaddage.personaddage.personaddage;//调用之后返回其p2本体;
空指针调用函数调用成员函数
利用一个空指针去调用类中的成员函数时,要注意传入的指针为空类型;成员函数中若有成员变量时;
将会发生报错,因为this指针为空类型,无法找到成员变量;
class people{
public:
//构造函数
people()
{
cout<<"people 的构造函数"<<endl;
}
//成员函数
void func(string name){
if(this==NULL)
return;
cout<<name;
}
//析构函数
~people(){
cout<<"people的析构函数"<<endl;
}
public:
string m_name;
string my_phone;
};
void test05(){
people *p=NULL;//空指针 类型为people
p->func("yupuning");//调用成员函数的成员变量时要注意指针不能为空
}
const修饰成员函数
常函数与常对象
常函数:
- 不能修改成员属性
当成员函数被const修饰时 ,即在成员函数后面加上const,修饰的this的指向,this指针指向的对象的值不能改变;若想修改改变变量的值,在成员变量前面加上关键字mutable;
常对象:
- 常对象只能调用常函数
在对象前加上const;变为常对象;与常函数相同,其成员的值不能被改变,若成员属性前加上了mutable则可改变;
常对象只能调用常函数(由于常对象的属性不能被改变,若调用其他成员函数可能会改变其成员的属性)
常对象不能调用普通的成员函数 ;
友元
友元概念:让一个函数或者类访问访问另一个类中的私有成员
全局函数做友元:
全家函数要访问私有属性时,在函数前加上friend 放在类中即可实现,该函数访问私有成员
类作友元:
让该类可以访问其它类中的私有属性;在类名前加上friend放在要访问的类中;
在类外也可以写成员函数;只需加一个作用域即可;还需要加上返回值类型;
成员函数作友元:
让成员函数可以访问该类中的私有属性;
在其前面加上friend 以及作用于哪个类下的类名;
运算符重载
加号运算符重载:
对已有类型进行重新定义,赋予其另外一种功能,以适应不同的数据类型
成员函数 实现加号运算符重载
Person Operator +(person &a)
{
Person temp;
实现类中对象相加赋值给temp;
Return temp;
}
全局函数实现加号运算符重载
Person Operator +(person &a,person&b)
{
Person temp;
实现类中对象相加赋值给temp;
Return temp;
}
此时就可以实现person p3=p1+p2;
成员函数重载加号本质为person p3=p1.operator+(p2);
全局函数重载加号本质为person p3=operator+(p2,p1);
两个类的相加;
函数运算符重载版本:
(实现person+int型)
Person Operator +(person &a,int num)
{
Person temp;
实现类中对象相加赋值给temp;
Return temp;
}
内置数据类型的表达式不能发生重载
不能滥用 运算符重载 加法写成减法或乘法,无法分辨
左移运算符重载
实现 输出自定义内容
只能利用全局函数重载左移运算符
Ostream & operator<<(ostream &cout,person& p) 本质为operator <<(cout,p);
{
Cout<<p中的成员变量;
Return cout;
}
简化为;
Cout<<p<<endl;(并且可以无限追加,实现链式编程,因为一直返回cout)
#include<iostream>
using namespace std;
class person{
friend ostream & operator<<(ostream &cout,person &p);
public:
int age;
private:
int birth;
public:
//成员函数实现重载
person operator+(person &p)
{
person temp(0,9);
temp.age=this->age+p.age;
temp.birth=this->birth+p.birth;
return temp;
}
person(int age,int birth){
this->age=age;
this->birth=birth;
cout<<"person的构造函数"<<endl;
}
};
//全局函数重载加号
/*person operator+(person &p1,person &p2)
{
person temp(0);
temp.age=p1.age+p2.age;
return temp;
}*/
//全局函数 实现重载<<
ostream & operator<<(ostream &cout,person &p)
{
cout<<"age="<<p.age;
cout<<"birth="<<p.birth;
return cout;
}
void test01()
{
person p1(10,10);
person p2(10,10);
person p3=p1+p2;
cout<<p3<<endl;
}
int main()
{
test01();
system("pause");
return 0;
}
重载++运算符
实现对象成员变量的自增;
class my{
friend ostream & operator<<(ostream &cout,my &p);
public:
my(int a){
this->a=a;
}
//重置前置加号
my & operator++()
{
this->a++;
return *this;
}
//重置后置++
my & operator++(int )
{
my temp=*this;
this->a++;
return temp;
}
private:
int a;
};
赋值运算符重载
编译器提供浅拷贝,对于开辟在堆区的数据,容易造成重复释放;
故我们应该自己提供一个赋值运算符重载,让其赋值的对象重新深拷贝;
来实现赋值操作
person &operator=(person &p){
//判断该指针是否为空
if(age!=NULL){
//如果不为空 则先释放 再重新申请一块内存
delete age;
age=NULL;
}
age=new int (*p.age) ;
return *this;
}
函数调用运算符重载 仿函数
非常灵活
通过在类中重载小括号实现实例化对象后直接调用函数;
可以实现多个功能
继承
类与类之间存在的特殊关系;除了他有自己的特性外还有共性;通过继承可以减少代码复杂;
在公共的特点上可以利用继承的技术,将其先放在一个类中;在其他类中去进行继承;
Class 子类(派生类) :继承方式 父类(基类)
{};
继承方式
- 公共继承
父类中的公共权限均可访问,私有权限子类无法访问与保护权限子类内可访问类外不可访问,访问类型不变为原类型
- 保护继承
父亲中公共权限均可访问,访问类型变为protected权限,私有权限不变化;保护继承变为保护权限,类外访问不到
- 私有继承
私有继承子类类外不可访问,变为私有权限;父类中私有成员子类类外不可访问;其余可以在类内访问;
变为私有权限后,再将其作为父类时,无法访问其成员变量;
继承中的对象模型
父类中的所有非静态成员都会被子类继承下去;
父类中的私有属性被隐藏,因此访问不到,但是确实被继承下去了。
继承构造和析构的顺序
先构造父类在构造子类;
析构时先析构子类在析构父类;
继承中的同名成员处理
直接访问该名为子类中的成员变量;
若要访问父类中的成员,需要加上作用域 base::变量名
如果访问同名的函数:
与成员变量相同 ,加上作用域即可;
如果子类中出现与父类相同的函数名,编译器会自动省略父类中相同的函数名;
若要访问该省略的函数,需要加上作用域;
静态同名情况下也是相同,但是作用在全局上,可以直接通过访问类名来调用函数或者成员;
也可以通过对象和作用域来访问;
多继承语法(一个类继承多个类)
若出现同名情况 加上作用域以区分
菱形继承
当菱形继承的时候,两个父类拥有相同的的数据,需要加以作用域以区分;
利用虚继承可以解决浪费问题;在继承之前加上关键字virtual变为虚继承;变为虚基类。
vbptr为虚基类指针,偏移的位置均为相同的地址,让数据只有一份
多态
内置数据类型的表达式不能发生重载
不能滥用 运算符重载 加法写成减法或乘法,无法分辨
标签:p1,函数,对象,成员,扩展,c++,int,内容,cout From: https://blog.csdn.net/2302_80554221/article/details/143769266