C++类与对象

C++的基本知识

&引用

类型名 & 引用名 = 变量名(这里只能是变量名,不能是式子)
int n = 4;
int & r = n;//r引用了n,r的类型是int &
某个变量的引用,等价于这个变量,相当于别名
int * & r = & n;

引用的特征
定义时即初始化:定义引用时一定要将其初始化成引用某个变量
一旦引用了某个变量,就不可能再引用别的变量啦
只能引用变量

应用

void swap(int & a,int & b){} //采用引用变量的交换函数

int & SetValue(){return n;}//对返回值的引用
int main()
{
	SetValue()=40; //函数返回值的引用
}

const

const 的不完全总结

定义常量

const 和 defeat 一样可以定义常量,但是const带有类型	
const char beijign = "huanyingni"

定义常量指针
不可以通过常量指针修改其指向的内容

int n,m;
const int *p = &n;//赋初值
*p  = 5; //试图修改,编译出错 

不能把常量指针赋值给非常量指针,反过来可以

const int *p1;
int *p2;
p1 = p2;//ok
p2 = p1;//no
p2 = (int *)p1;//ok 强制类型转换

函数参数为常量指针时,可避免函数内部不小心改变参数指针所指的地方内容

void My(const char*p)//p的类型为{
	strcpy(p,"this");//出错
}

定义常引用

const int & r = n;
r是const int &类型
不能通过引用去修改其引用内容

常引用和非常引用的转换

int & 类型的引用或int类型的变量也用来初始化const int &类型的引用
const int 类型的常变量和const int &类型的引用则不能用来初始化int &类型的引用,除非进行强制类型转换

函数声明中加const

const int * fun2() //调用时 const int *pValue = fun2();
                  //我们可以把fun2()看作成一个变量,即指针内容不可变。
int* const fun3() //调用时 int * const pValue = fun2();
                 //我们可以把fun3()看作成一个变量,即指针本身不可变。

C++规定只能使用指向常量的指针来存放常量对象的地址——这就是加const的作用
在函数形参表中,如果引用参数和指针参数,参数应是const类型
常指针:指针本身是常量不可变
char* const p;
指针常量:指针所指向的内容是常量不可变
const char *p;

const修饰成员函数

class A{
	void function() const; //常成员函数, 它不改变对象的成员变量.                        
						//也不能调用类中任何非const成员函数。
}

更完整的const用法

new delete

new实现动态内存分配
分配一个变量

int *P
P = new int;
动态的分配出sizeof(int)大小的空间给P
并将这个空间的起始地址给P

分配一个数组

int *P
P = new int[N]
N可以是整型表达式,即可以使变量
动态的分配出N*sizeof(int)大小的空间给P
new intnew int[N]的返回值类型都是int *

delete释放空间
释放单个变量

delete p;

释放数组

delete []P;

!!!空间只能释放一次,不然会导致出错

内敛函数机制

将整个函数的代码插入到调用函数语句处,不会产生调用函数的语句
定义方法:函数前缀加 inline
执行程序的体积增大

函数重载

名字相同,参数类型或个数不同的一个或多个函数。

函数的缺省参数

函数定义时可以让最右边的连续若干个参数有缺省值

void func(int x1, int x2=2,int x3=3){}
	func(10);//等价于func(10,2,3)
	func(10,8);//等价于func(10,8,3)
	func(10,,8);//error,只能最右边的连续若干个参数缺省

类与对象

基本概念和注意事项

面向对象的程序 = 类+类+...+类
类 = 数据结构+操作该数据结构的函数	
对象所占用的内存空间大小=所有成员变量的大小之和
所有的对象共用成员函数,所以对象所占用的内存空间不包括成员函数
类的成员 = 成员变量+成员函数
对象间的运算可以用= 进行赋值,其他的运算符号需要“重载”

类的定义

class CRectangle{
	public:
	..............若干个变量和函数
	private:
	..............若干个变量和函数
};//必须有符号

使用类的成员变量和成员函数

对象名.成员名


指针->成员名
CRectangle r1,r2;
CRectangle * p1 = & r1;
CRectangle * p2 = & r2;
p1->w = 5;
p2->Init(5,4);//让Init这个函数作用在p2上面

引用名.成员名
CRectangle r2;
CRectangle & rr = r2;
rr.w = 5;
rr.Init(5,4);

类的成员函数和类的定义分开写

class CRectangle
{
	public:
		int w,h;
		int Area();//这里没有给出成员函数体,拿到外面去写
		void Init(int ww,int hh);
};
int CRectangle:: Area(){ //主要两个冒号,表明是这个类的成员函数
	return w*h;
}

类成员的访问权限

class classmate
{
	int n;//没有指明,默认为私有
	private:
		私有属性和函数
	public:
		公有属性和函数
	protected: 
		保护的属性和函数
}
struct classmate{
	int n;//没有指明,默认为公有
}
private成员:可以被下列函数访问 
	– 此类的成员函数 
	– 此类的友元函数
public成员:随便哪里都可以访问
protected成员:可以被下列函数访问 
	– 此类的成员函数 
	– 此类的友元函数 
	– 此类派生类的成员函数

成员函数的重载和参数缺省

使用缺省参数要注意避免有函数重载时的二义性
	void valuex(int val = 0){
		x = val;
	}
	int valuex(){
		return x;
	}
	A.valuex();//不知道调用哪个

构造函数

函数名与类名相同
无返回值,无void
一个类可以有多个构造函数:重载
完成初始化工作:一个对象应该初始化再使用
类一定有构造函数,没定义就默认一个
定义了系统就不在生成默认的啦(JAVA是不是这样)
对象生成时自动调用构造函数,对象一旦生成,这个对象再也不能调用构造函数

定义格式

class A{
public:
	A(int x,int y=0);//构造函数
};
A::A(int x,int y=0){
	a = x;b = y;
}

生成单个对象

有默认构造函数时采用下面方法:
A a;//这样搞完能直接用,注意和JAVA的区别
A *p = new A;
无默认构造函数时采用下面方法:
A a(1);//直接赋值——缺省
A *p = new A(a,2);//动态初始化对象,这个才和JAVA类似

生成多个对象——数组

构造函数在数组中的使用
class Test{
	public:
		Test(int){}
		Test(int n,int m){}
		Test(){}
}
Test arr[3] = {1,Test(1,2)};//分别依次使用了构造函数,1是临时对象
这个是指针数组
Test *p[3] = {new Test(4),new Test(1,2)}//这里只会生两个对象,因为p[2]指针没有指向内容

复制构造函数

参数只能是同类对象的特殊构造函数
只有一个参数,即对同类对象的引用
结构:X::X(X & x)或X::X(const X & x)后者能以常量对象作为参数
参数必须是引用!	
如果没有定义,系统生成默认复制构造函数,完成两个对象的复制功能
自己定义,系统就不会生成默认的复制构造函数

定义格式

class A{
	int a;
}
A a1;//调用默认构造函数
A a2(a1);//调用默认复制构造函数,将a2初始化成a1

class A{
	int sum;
	A::A(const A & a){
		sum = a.sum;
		cout<<"copy";
	}
}
A a1;
A a2(a1);//有一个输出

复制构造函数使用时机
用一个对象初始化另一个对象

A a2(a1);//定义的同时赋值叫做初始化,这是调用的是复制构造函数
A a2 = a1;//初始化语句,调用的也是赋值构造函数并不是赋值

函数参数是一个类的对象

如果某函数有一个参数是类A的对象,调用函数时,类A的复制构造函数将被调用
实际上是把实参作为复制构造函数的参数得到一个实参的拷贝,有时候不是拷贝——取决于是怎么的复制构造函数
也就是把实参作为参数遵循复制构造函数的方法生成一个形参

函数的返回值是一个类的对象

这个返回对象用复制构造函数初始化,比如返回一个b
那么复制构造函数的参数就是b
也就是把返回值作为参数遵循复制构造函数的方法生成一个对象

常量引用参数的使用

void fun(A a){//生成形参时会调用复制构造函数
	cout<<"hello";
}
void fun(const & A a){//不会调用复制构造函数
	cout<<"hello";//使用const A & a防止改变实参
}

对象间的赋值不会调用复制构造函数

类型转换构造函数

目的:实现类型的自动转换
只有一个参数且不是复制构造函数,就可以看成转换构造函数
当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象
class A
{
	double a,b;
	A(double x,double y){
		a = x;
		b = y;
	}
	A(int x){
		a = x;
		b = 0;//如果这个没有对b赋值的话,对象的b就是未初始化的值
	}
}
int main(){
	A a1(7,8);
	A a2 = 12;//这里就调用了一个转换构造函数
	a1 = 9;//9被转化临时的类A的对象 
	//这里的a1中的a=9,b=0;
}

析构函数

在对象消亡的时候起作用
没有参数和返回值
一个类只能有一个返回值
名字和类名相同,在前面加~
没有自定义的时候,系统会自动生成一个
自定义后,系统不再自动生成啦
自定义析构函数的作用:在对象消亡前做一些善后工作,如释放空间

调用时机
直接生成的对象直接自己消亡

class A
{
	public:
	~ A(){
		cout<<"dead"<<endl;
	}
};
int main(){
	A a[2];
	cout<<"next"<<endl;
	return;
}

new出来的对象,必须用detele消亡

class A
{
	public:
	~ A(){
		cout<<"dead"<<endl;
	}
};
int main(){
	A * a = new A;
	delete a;
	cout<<"next"<<endl;
	return;
}

析构函数在对象作为函数返回值的时候被调用

class A
{
	public:
	~ A(){
		cout<<"dead"<<endl;
	}

};
A a;
A fun(A a){//这个函数里面的对象参数随着函数的结束而消亡,调用析构函数
	return a;//这里返回的临时对象
}
int main (){
	a = fun(a);//这个返回的临时对象被用过之后会消亡,调用析构函数
	return;
}

典型类函数总结描述

自定义函数的内部过程:
形参的生成:把实参作为参数,调用复制构造函数,复制出来一个形参
形参作为函数内部的参数,随着函数结束而消亡,调用析构函数
返回的时候会把函数内部的参数作为复制构造函数的参数 复制出来一个临时参数
在主函数中,临时参数使用完毕之后,要消亡,调用析构函数

例子

#include<iostream> 
using namespace std;
class Demo{
	int id;
	public:
		Demo(int i);//是类型转换构造函数
		~Demo();
};
Demo::Demo(int i){
	id = i;
	cout<<"id="<<id<<"生成"<<endl; 
}
Demo::~Demo(){
	cout<<"id="<<id<<"消亡"<<endl; 
}

Demo d1(1);//d1生成 

void Func(){
	stati Demo d2(2);//局部静态变量调用构造函数,d2生成 
	Demo d3(3);//d3生成 
	cout<<"func"<<endl;//输出func 
}//函数结束只有d3消亡,静态局部变量在整个程序结束的时候消亡 

int main(){
	Demo d4(4);//d4生成 
	d4 = 6;//调用构造函数,生成临时对象,临时对象消亡的时候,调用析构函数 
	cout<<"main"<<endl;//输出main 
	{
		Demo d5(5);//d5 生成 
	}//d5 消亡 
	Func();
	cout<<"main ends"<<endl;//输出main ends 
	return 0;//d4 消亡 
	//d2 消亡
	//d1 消亡
	//谁先构造谁后析构 
}

this指针

this指针的诞生
当C++翻译到C语言的时候,类翻译成结构体
成员函数被翻译为全局函数,且形参列表多一个参数————this指针
这个指针就是一个指向当前对象的指针变量

在C++中的使用

指向成员函数所作用的对象

A{
	public:
		double a,b;
	A get(){//将一对象的ab++后返回这个对象
		this->a ++;
		this->b ++;
		return *this;
	}
}

C++语句在执行的时候翻译成C语言语句

A{
	int i;
	public:
		void Hello(){cout<<"Hello"}//如果这个函数里面有i就会出错
}		//void Hello(A *this){cout<<"Hello"}
int main(){
	A *p=NULL;
	p->Hello();//并不会出错
}//Hello(p);

静态成员函数不能使用this指针
因为静态成员函数并不具体的作用于某个对象
因此静态成员函数中真实的参数个数就是程序中写出的参数个数

this是一个常量指针
常量指针是说指针的值是const的
指针常量是说指针指向的变量的值是const的
常指针常量看字面就知道是把常量指针和指针常量合在一起,不但指针本身是const的,而且指针指向的变量也是const的

静态成员——前缀为static

相关描述
普通的成员变量每个对象各有一份,而静态成员变量只有一份,所有对象共享(静态成员函数没有这个性质)
sizeof不会计算静态成员变量的大小
普通成员函数必须具体作用在哪一个对象上,静态成员函数并不具体作用在某一对象上
静态成员不用但可以通过对象访问。
静态的也就意味是全局的
静态放在一个类中,虽然是意味着是全局的,但也只能这个类调用:类的静态成员。
静态成员若是私有的。也不能在main 中访问
静态成员函数不能访问非静态成员变量和非静态成员函数。因为静态可以再生成对象前使用,而非静态必须在对象生成之后使用
静态成员函数主要作用是用来访问同类中的静态成员
在类中声明static 变量的时候不分配空间,要在外面单独声明时才分配空间
格式:

class A{
	static int n;//声明
};
int A::n=0;//需要在外面再次初始化

访问静态成员

类名::成员名
A::Hello();
A::i;
对象名.成员名
A r;r.i;r.Hello();//虽然这样调用,但还是和对象没有关系
指针->成员
A *p = &r;
r->Hello();
引用.成员名
A &ref = r; ref.r;

在静态变量时遇到的问题:
对象创建的时候,使用构造,令静态变量++;
消亡的时候,使用析构,令静态变量–。这样可以记录现在的对象数量

但是这样会遇到问题:在以类A的对象为函数形参或者函数返回值的时候,使用了复制构造函数,没有使静态变量++,但是在这些对象消亡的时候必须要经过析构函数,这样就使静态变量–。
解决办法:
编写一个复制构造函数

封闭类

成员对象:类的一个数据成员是另一个类的对象
封闭类:有成员对象的类是封闭类

封闭类的初始化:

在封闭类生成对象的时候,里面的成员对象也要生成,要注意成员对象的构造函数的使用
即让编译器明白成员对象怎么初始化
封闭类的构造函数的初始化列表:专门用来对成员对象进行初始化,当然也可以对其他的数据成员进行初始化
成员对象的初始化列表中可以是任意复杂的表达式,可以包含函数、变量
class A{
	private:
		int a;
		int b;
	public:
		A(int x,int y):a(x),b(y){}//初始化列表
}
class B{
}
class C{
	private:
		A a;
		B b;
		int c;
	public:
		C(int x,int y,int z):a(x,y),c(z){}
}

封闭类构造函数和析构函数的执行顺序

封闭类对象生成时,先执行所有对象成员的构造函数(也就是说或先执行初始化列表中构造函数),最后在执行封闭类的构造函数
对象成员的构造函数调用次序和对象成员在类中的说明顺序一样,与他们在初始化列表中的顺序没有关系
也就是说先执行封闭类的初始化列表在去实现构造函数函数体里面的内容
先构造的后析构。先初始化的后消亡
所以封闭类先析构,类中的对象成员后析构————因为封闭类的析构函数可能会调用对象成员

封闭类的构造函数

class A {
	public :
		A() {cout<<"default"};
		A(A & a) {cout<<"copy"};
}
class B{
	A a;
}
int main(){
	B b1,b2(b1);//这样的话,就会调用A中的复制构造用b1.a初始化b2.a
	return 0;
}

常量对象

不希望对象的值改变的时候,定义该对象的时候可以再前面加const,即为常量对象
不希望成员函数有修改动作时。在函数后缀中加入const,即为常量成员函数
常量成员函数执行期间不修改所作用的对象——在常量成员函数中不能修改成员变量的值(静态成员除外)
也不能调用同类的非常量成员函数(静态函数除外——静态函数内不会访问非静态的)
常量对象不可以调用非常量的函数,
以上种种限制归根结底是常量不能修改,只要涉及到有可能修改的都不能调用

const 涉及的重载
在一个类中,允许出现两个一模一样的函数,仅有const的区别
在调用的时候,常量对象就可以调用const定义的函数

常引用——(const & A a)
当对象作为函数形参的时候使用
引用可以不调用复制构造函数
const可以避免引用带来的可能修改对象的危险

友元

友元函数

一个类的友元函数可以访问类的私有成员
友元函数不是这个类的成员函数,可以是另一个类的成员函数和构造函数、或者是全局函数
class B{
	public:
		B();
		void func();
}
class A{
	friend B::B(){/*函数体*/};
	friend void B::func(){/*函数体*/};
}

友元类

如果类A是B的友元类,那么A的成员函数就可以访问B的私有成员
友元类关系不能传递不能继承
友元关系不是相互的
class B{
	private:
		int b;
		B(){};
	friend class A;//A 是 B 的友元类
}
class A{
	B x;//这里就可以直接调用B中的私有构造函数。而在main中这样是不允许的
	int ret(){x.b++}; //可以访问B中b,访问的时候也要先生成一个变量
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值