1、this指针与类与对象的内存结构分布
1)this指针定义在类的方法中
2)是隐性的指针
3)创建类对象之后,this指针没有具体指向哪里,如果不调用类的方法,,this指针不会指向操作函数。
当类对象调用类的方法去操作本身内存空间时,this指针会指向这个类的对象。
4)是属于类的唯一的一个指针,在对不同的类对象操作时会实时切换。
app.h
#ifndef APP_H
#define APP_H
#include <iostream>
using namespace std;
class point
{
private:
int x;
int y;
public: //操作方法一般都设为公有,被外部函数调用
//普通的类的成员函数
void setxy(int a,int b)
{
this->x=a; //this指针是隐藏的,可以不用写
this->y=b;
}
void disxy()
{
cout<<"x= "<<x<<endl;
cout<<"y= "<<y<<endl;
}
};
#endif // APP_H类是存在test段,
main.cpp
#include"app.h"
int main()
{
cout << "Hello World!" << endl;
point p; //分配一段内存空间,没有内容,需要初始化
p.setxy(18,27);
p.disxy();
return 0;
}
p是个数据类型,局部变量。执行point p语句根据类的数据成员为p分配内存空间。可以用size查看空间大小。
p.setxy是一个代码的起始地址。
2、构造与折构函数(拷贝构造)
2.1构造函数:是用来对类的对象的内存中的变量进行初始化
#include <iostream>
using namespace std;
class point
{
private:
int x;
int y;
public:
//定义了一个类的构造函数,用于初始化
point (int a,int b)
{
cout<<"constructor"<<endl;
x=a;
y=b;
}
/*
void setxy(int a,int b)
{
this->x=a;
this->y=b;
}*/
void disxy()
{
cout<<"x= "<<x<<endl;
cout<<"y= "<<y<<endl;
}
};
int main()
{
cout << "Hello World!" << endl;
point p(20,90); //直接调用类的构造函数
//p.setxy(18,27);
p.disxy();
return 0;
}输出: constructor
x=20
y=90
特点:1)定义的构造函数,会对类对象创建时自动调用构造对对类对象成员进行初始化
2)函数名跟类名要一样
3)没有返回值
4)如果定义在类外,也要加上类从属关系
5)构造函数可以重载(函数名一样,但传参不同)
何为重载:示例
point()
{
cout<<"no param"<<endl;
x=66;
y=45;
}
point(int a)
{
cout<<"one param"<<endl;
x=a;
y=88;
}
point (int a,int b)
{
cout<<"constructor"<<endl;
x=a;
y=b;
}
point p;
p.disxy();
创建类对象的时候会在类中查找构建函数,如果没有定义构造函数,系统会自动分配一个无传参的构造函数。运行的时候不会报错。如果有,传参不一样会出现报错。
2.2析构函数:对已经存在的类对象的内存进行清除处理
~point()//在类里面定义析构函数
{
cout<<"clean all"<<endl;
}
1)无传参,无返回值,但是要带一个“~”声明是析构
2)每调用一次构造函数创建一个类对象,就一定会在退出前自动调用析构函数来清除这个对象内存。
3)如果类没有定义析构函数,系统也会自动分配一个析构函数。
2.3拷贝构造函数
1)将一个变量的内容拷贝过程,初始化到新的变量的过程
类里面定义拷贝构造函数
point(const point &p)
{
cout<<"copy construnstor"<<endl;
x=p.x+11;
y=p.y+44;
}
point p2 = p1; 或者point p2(p1);都是调用了上面的函数
2.4深浅拷贝构造函数
1)浅拷贝vs深拷贝
char *pdest; //指针指向一块内存地址,可能有内容。(类成员)
main.cpp: point p2 = p1; //拷贝函数
// 定义一个拷贝构造函数
point(const point &p)
{
cout<<"copy constructor"<<endl;
xres = p.xres;
yres = p.yres;
//会使得p1与p2共享到同样一块内存,这种做法称为浅拷贝构造
//调试1:
//pDest = p.pDest;
//深拷贝做法
//调试2:
pDest = new char[strlen(p.pDest) + 1];
if(pDest != 0)
{
strcpy(pDest, p.pDest);
}
}
//析构函数需要释放内存
~point(){
cout<<"clean all"<<endl;delete pdest;}
浅拷贝隐藏bug,很少使用。
3、初始化列表
3.1类成员函数的实现
通过类对象调用类的成员函数,实现初始化。
p.setxy();
3.2构造函数实现
//用于初始化的构造函数
point (int a,int b):x(a),y(b){} //这样的形式,效率比较快
创建类对象时自动调用构造函数,实现初始化。
a.一个是构造,一个是普通成员函数
b.构造函数在分配内存空间的就初始化,而成员函数是先定义一个类的对象分配内存空间,再对成员初始化,频率是不一样的。
特点:更快实现对类对象的各个变量初始化
什么情况下会使用初始化列表?
1.使用const声明的变量要初始化、
2.使用引用声明的变量要初始化
const int x;
3.派生类中要对基类部分内容进行初始化
class space:public point
{
private:
int z;
public:
void setz(int c);
}
space::space(int c):point(x,y)
{
z = c;
}
4、关键字分析
4.1static关键字
1)可以用于声明类的数据成员
class point
{
private:
int x; //非静态数据成员
static int y; //静态数据成员
}
如果static声明后,编译器会在编译时将这个变量连接到data段。
好处:在编译阶段就会被处理(分配得到自己的空间);
放到data段,在加载后是独立存在的段,被所有的程序都可以访问到,相当于全局变量。
如何使用:
类外初始化
用static声明
可以直接使用:如果是private声明,只能通过类的公有函数操作;如果是public声明,可以直接通过类调用,也可以通过类的公有函数操作。
#ifndef INIT_H
#define INIT_H
#include<iostream>
#include<string>
using namespace std;
class computer
{
private:
float unit_price; //非静态成员函数
//static float total_price; //静态成员函数
public:
static float total_price;
computer(int price)
{
unit_price = price;
total_price += price;
}
void disinfo()
{
cout<<"unit_price = "<<unit_price<<endl;
cout<<"total_price = "<<total_price<<endl;
}
};
float computer::total_price = 0;
#endif // INIT_H
int main()
{
cout << "Hello World!" << endl;
computer::total_price = 288; //直接通过类调用
cout<<"total_price:"<<computer::total_price<<endl;
computer p1;
p1.disinfo(20);
return 0;
}
2)可以用于声明类的成员函数
与普通的成员函数相比,静态成员函数具有如下特点:
只能操作静态的成员(静态的数据成员与静态成员函数)
3)与全局变量相比较,用static声明有什么好处
a、具有全局变量的功能
b、具有很好的封装性,具有更高的安全性
4.2const关键字
1)声明类的数据成员
const int id;(只读,不能修改),可以使用列表进行初始化。
student (int stuid, int s...) : id(stuid),sorce(s)
特点:a、必须使用初始化列表进行初始化
b、只能使用成员函数读,不能写
c、
2)声明类的成员函数
void set(string name) const
特点:
a、const函数只能读,不能有写的操作
b、可以被const类对象调用
3)声明类的对象
示例:const student stu 或者 student const stu
特点:
a、声明之后就是一个常量,不允许再修改。
b、可以使用构造函数进行构造
c、不能调用非const成员函数,可以调用const成员函数
........
5、友元操作
5.1 友元函数 一个变量包装成一个类里面的数据成员 ==> 类的封装性,具有很好的安全性
class point
{
private:
int xres;
public:
void setx(int x)
{
xres = x;
}
}
为了提高操作数据成员的效率,允许外面普通函数直接到类里面的数据进行操作,而不需要通过成员函数,这就是友元操作 这个普通函数必须得到类的认可。这个函数就是类的友元函数 如何将普通函数变成一个友元函数,并且访问类的数据成员 1. 得有一个类,类中定义数据成员 2. 得有一个普通函数 3. 在类中通过friend声明为类的友元函数 4. 直接在类外面,利用友元函数操作类里面的内容
#include <iostream>
#include <string>
using namespace std;
class student
{
private:
string name;
const int stuid;
float score;
public:
student(string n, int i, float s):name(n),stuid(i),score(s)
{
}
friend void display(const student &s);
};
//一个普通函数
void display(const student &s)
{
cout<<"name:"<<s.name<<endl;
cout<<"stdid:"<<s.stuid<<endl;
cout<<"score:"<<s.score<<endl;
}
int main()
{
student stu("kitty", 102, 89);
display(stu);
return 0;
}
5、2友元类
#include<iostream>
#include<string>
using namespace std;
class space; //类space未定义之前就已经在类point中用到,需要提前声明,以防出错
class point
{
private:
int xres;
int yres;
string name;
public:
point(int x, int y, string n)
{
xres = x;
yres = y;
name = n;
}
// point成员函数
void setname(space &s);
void display()
{
cout<<"point_name: "<<name<<endl;
}
};
class space
{
private:
int xres;
int yres;
int zres;
string name;
public:
space(int x, int y, int z, string n)
{
xres = x;
yres = y;
zres = z;
name = n;
}
void display()
{
cout<<"space_name: "<<name<<endl;
}
//声明友元
//friend void point::setname(space &s);
friend class point; //类point的所有成员函数都是类space的友元函数,能存取类space的私有成员和保护成员
};
void point::setname(space &s)
{
name = "kitty";
s.name = "pitter";
//name = s.name;
}
int main()
{
point p(10, 20, "yang");
space s(100, 200, 300, "space");
p.setname(s);
p.display();
s.display();
return 0;
}
使用友元类时注意:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明