类和组合
对象数组和对象指针
可以进行定义对象数组和对象指针
一维对象数组定义方法
类名 数组名[下标表达式];
类名 数组名[下标表达式]={类名(…),类名(…)};
//第一种默认调用系统给的无参构造函数
//第二种通过后面{}决定
class A
{int x,y;
public:
A(){x=0,y=0;}
A(int x,int j=0){x=i,y=j;}
};
如果这样定义的话
A obj[4]={1,2};
//如何传递?obj[0]出态为x=1,y=0
//obj[1]初态为x=2,y=0
对象数组成员的引用同样注意限制性
————————————————————
对象指针
对象所占据的内存空间用于存放数据成员,对象的存储空间的起始地址就是对象的指针!!! 定义方法
类名 *对象指针名
访问方法
(*对象指针名).成员名
对象指针名->成员名
!!但要注意访问是否合法还要保证访问成员是公有成员!!
所有指针必须先赋值才能被使用
!!对象指针也是指针 和其他指针使用方法是一样的!!
this指针----自引用指针
①当对象被赋初值之后,就会出现一个隐藏的指针,这个对象的this指针就??指向!!内存中保存该对象数据的存储空间的首地址
②另外在类的成员函数中是可以使用这个this 指针的
class CMyclass
{ private:
CMyclass *this;
...
public:
CMyClass(){...}
...
};
一般不使用 使用的话就是需要在成员函数的实现中访问this所指的对象本身,而不是其各个成员
比较不错的例子 理解this指针
#include<iostream.h>
class ThisSample
{ int n;
public:
void SetValue(int m){n=m;}
void AddValue(int m)
{ThisSample q;//函数内部临时对象????
//???
q.n=n+m;//n相当于this->n n+m赋给了q.n
*this=q;//把q复制给this指针所指的那个对象
//没调用复制构造函数
}
void Display()
{cout<<"n="<<n<<endl;}
void main()
{ ThisSample s;
s.SetValue(10);
s.Display();
s.AddValue(5);
s.Display();
}
————————————————————
类的组合
就是类中内嵌对象成员
面向对象:复杂对象进行分解抽象,复杂对象分解为简单对象的组合。
对象成员的初始化
成员初始化列表:
例 CPoint::CPoint(int xx,int yy):x(xx),y(yy)
{cout<<“constructor called”;}
!调用构造函数时先执行成员初始化列表 再按顺序执行构造函数体内语句
!与说明普通对象不同,在说明对象成员时并没有创建该对象,等创建外层类的实例时才会为对象成员分配内存初始化
!类中有对象成员,先执行所有对象成员的构造函数,再执行当前类的构造函数体
代码理解:
class CPoint
{private:
int x,y;
public:
CPoint(int i=0,int j=0){x=i,y=j;}
CPoint(CPoint &p);
int getx(){return x;}
int gety(){return y;}
};
CPoint::CPoint(CPoint &p)
{x=p.x;y=p.y;
cout<<"复制构造函数调用"<<endl;
}
class CDistance
{ private:
CPoint p1,p2;
double dist;
public:
CDistance(CPoint xp1,CPoint xp2);
};
//进行构造函数的定义
CDistance::CDistance(CPoint xp1,CPoint xp2):p1(xp1),p2(xp2)//成员初始化列表
{ cout<<"CDistance构造函数被调用"<<endl;
double x=1;
double y=2;
}
void main()
{CPoint myp1(1,1),myp2(4,5);
CDistance myd(myp2,myp2);
}
思考:怎么输出呢??
Thought:
main函数先定义了两个类对象
其次进行CDistance初始化
CDistance构造函数是怎样被调用的呢?
NO.1:首先进行形实结合 把myp1 myp2赋给
构造函数的形参xp1 xp2
==这时会自动调用CPoint的复制构造函数==
NO.2:接下来出现在成员初始化列表中,用已存在的xp1,xp2对象初始化成员初始化列表中的形参
—————————————————————————
位运算
按位与(&) 按位或(||) 按位抑或 按位取反 移位
C++中有两个移位运算符:左移运算(<<)和右移运算(>>),都是二元运算符。
左移是按照指定的位数将一个数的二进制值向左移位,左移后,低位补0,移出的高位舍弃。
右移是按照指定的位数将一个数的二进制向右移位,右移后移出的低位舍弃,如果是无符号数则高位补0;如果是有符号数,则高位补符号位
① 例:一int型变量a的值为-8,则a在内存中的二进制补码值为11111000,于是表达式a>>2的值为-2,补码变为11111110,对应值为-2
②例:表达式2<<1的值为4,移位前2的补码值为:00000010,移位后补码变为000000100,值为4
移位运算的结果是位运算表达式(a>>2和2<<1)的值,移位运算符左边表达式的变量值并不会改变
——————————————————————————
静态数据成员的声明和使用:
①类中声明 例:static int total;
②类外进行(必须) int CPoint::total=0;
【不能在构造函数中初始化 理解下】
访问:
①公有的: 类名:: 或 对象名. 引用
②私有的:只能在类内引用,类外没法用(因为是私有的)
静态成员函数
声明:
前面加static
访问:
①类名:: 访问
②对象名. 访问
说明:
静态成员函数可以直接引用该类的静态数据和成员函数,而不能直接引用非静态数据成员
(静态成员函数定义类就存在了 非静态数据成员实例化对象时才存在 可以这么理解吧)
静态成员函数引用非静态数据成员,通过参数传递得到对象名,通过对象名引用间接实现
例
class CTest
{int x;
public:
static void f(CTest a)
{ cout<<x<<endl;//错误!
cout<<a.x<<endl;
}
};
静态成员函数无this指针
练习
#include<iostream.h>
class CPoint
{ int x,y;
static int n;
public:
构造函数
CPoint(int xx=0,int yy=0)
{x=xx;y=yy;n++;}
复制构造函数
CPoint(CPoint &p)
{x=p.x;y=p.y;n++;}
析构函数
~CPoint(){n--;}
int get_x(){return x;}
int get_y(){return y;}
静态函数
static void get_n()
{coun<<"obj"<<n<<endl;}
};
静态数据成员初始化
int CPoint::n=0;
————————————————————————————
友元
【B类内嵌A类对象,但是B的成员函数没法直接访问A的私有成员】
友元:
<1>某类中嵌套一个类,该类普通成员函数可以访问那个类中的数据
<2>一个普通函数可以访问那个类中的数据
实现
①友元函数(普通函数+其他类的成员函数)
<1>friend <类型标识符> <友元函数名>(参数表)
<2>friend <类型标识符> 类名:: <友元函数名>(参数表)
②简单使用 普通函数
<1>
class CPoint
{ double x,y;
public
CPoint(double xx=0,double yy=0)
{x=xx;y=yy;}
double getx(){return x;}
double gety(){return y;}
//声明,在哪声明都行。这样想,它只是我的朋友,故不是本类的成员函数
//所以放哪声明都行,跟类无关,只是为了用它来访问类中私有数据
friend double get_distance(CPoint &p1,CPoint &p2);
};
double get_distance(CPoint &p1,CPoint &p2)
{
//传对象引用可以访问私有数据和公有数据
//!!这时候是友元函数起作用!!
return p1.x+p1.y+p2.x+p2.y;
}
void main()
{
CPoint my(1,1),my2(1,1);
//调用也是直接调用,因为这个函数只是那个类的朋友,不是本类的成员函数
cout<<"cout"<<get_distance(my,my2);
}
<2>(跟<1>差不多)
class CPoint
{ double x,y;
public
CPoint(double xx=0,double yy=0)
{x=xx;y=yy;}
double getx(){return x;}
double gety(){return y;}
friend double get_distance();
};
double get_distance()
{ CPoint p1(1,2);
CPoint p2(2,3)
return p1.x+p1.y+p2.x+p2.y;
}
//调用也是直接调用,因为这个函数只是那个类的朋友,不是本类的成员函数
③简单使用(感觉知道就行) 其他类的成员函数
class CPoint;
class CTest
{ ...
friend double get_distance(CPoint &p1,CPoint &p2);
...
};
class CPoint
{
CPoint p1;
CPoint p2;
//声明
friend double CTest::get_distance(CPoint &p1,CPoint &p2);
};
double CTest::get_distance(CPoint &p1,CPoint &p2)
{
...
}
//调用不是直接调用,得先构造CTest对象,通过**对象.**的形式调用。
总结:
其实差不多,A类中的友元函数可以是普通的函数,也可以是其他类的函数,不同情况对应不同的声明,只要明白一点,友元函数不属于A类本身,它只是用于调用A类的私有数据成员罢了,这时刻记住。
④简单实用 友元类
friend class <友元类名>
其实就是范围扩大了,A类为B类友元类,就在B类中声明:friend class A;,A类就是B类的朋友了,A类中所有函数,数据成员对于B类中的私有数据啥的都能访问,③扩展
class CPoint;
class CTest
{ ...
double get_distance(CPoint &p1,CPoint &p2);
void test(CPoint &p1);
...
};
class CPoint
{
CPoint p1;
CPoint p2;
//声明友元类
friend class CTest;
};
//调用不是直接调用,得先构造CTest对象,通过**对象.**的形式调用。
总结:
分为友元类和友元函数 懂得如何去理解
————————————————————————————
常类型
const修饰符
例:#define的不安全性
#include<iostream>
using namespace std;
main() {
int a=1;
//define只是字面替换 在预编译时进行了字符替换
#define T1 a+a
#define T2 T1-T1
cout<<"T2 is "<<T2<<endl;
return 0;
}
//输出结果为T2 is 2
const使用
#include<iostream>
using namespace std;
main() {
int a=1;
//这个常量是有类型 占用存储单元,有地址,可以用指针指向它
const T1=a+a
const T2=T1-T1
cout<<"T2 is "<<T2<<endl;
return 0;
}
//输出结果 T2 is 0
const与指针组合使用
//(1)常量指针:是指*指针变量**指向**常量**的*。
const char *pc="abcd";//声明指向**常量**的指针
右左法则(pc is a pointer to const char)
pc[3]="X";//编译时出现错误
pc="efgh"//正确,指针本身是一个**变量**
//(2)指针常量:是指**指针的常量**,指向**变量**的。
char * const pc="abcd"; //指针常量 必须在声明的时候进行初始化
右左法则 (pc is a const point to character)
pc[3]="x";//正确
pc="efgh";//编译时出现错误
//(3)指向常量的指针常量:指针是一个常量(不可以修改指向,指针指向的也是一个常量)
const char *const p="abcd";
const修饰做函数形参
//const <类型标识符> &<引用名>
void display(const int &r)
{
cout<<"r="<<++r<<endl;
//错误! 常引用没法改变
}
//有时会用到 这个应该用的比较多
const对象 const函数(知道就行)
const <类名> <对象名>
//必须同时初始化 实例化对象啥都没法改变(数据成员)
//声明const对象有啥用? 好像没啥用
void diaplay() const;
//const对象可以调用const修饰的成员函数。。
//用的少 开发可能用
说明:如果const修饰int型常量,则关键字int可以省去。
常量一旦建立,程序中任何地方则不能再改
const常量可以有自己的数据类型
函数参数也可以用const说明,用于保证实参在该函数内部不能改动
——————————————————————————
动态内存分配
C语言中动态内存分配有malloc colloc函数吧
1. new运算符
char CBuffer = new char[256];*
*CBuffer[1]=‘a’;
(CBuffer+5)=‘c’;
–成功:T类型的指针,指向新分配的内存 并且分配的内存空间是连续的
–失败:0(NULL)
2.delete运算符 就是释放内存
int *pInt = new int;
delete pInt;//删除单个指针**
char *CBuffer = new char[256];
delete []CBuffer;
3.面向对象中
class R
{
R(){ }
};
void main()
{
R *p;
p=new R();
cout<<p->area()<<endl;
//Java中摒弃了指针 多好。。
delete p;
//释放p指向的内存空间 会调用析构函数
}
**说明:**delete删除的只能是new运算符创建的对象
这两使用比较灵活