C++学习笔记3_类.和相关函数

1. 类
*在C++中,struct和class没有明显差别,不同C#,class一定要new(手动开辟内存)出来
struct Hero
{
char name[64];
int sex;
}
void print(struct Hero &h)
{
...
}
class AdvHero
{
public:
char name[64];
int sex;
void print()
{
cout<<name<<endl;
}
}
int main(void)
{
Hero h;
strcpy(h.name,"zhangsan");
//
AdvHero v;//不用new出来,c++中的new另有含义
strcpy(v.name,"lisi");
}
2. 构造函数:
class A
{
public:
int age;
char* Name;
A(int x)
{
age=x;
Name =(char*)malloc(100);
}
~A()
{
//析构函数没有形参,当对象被销毁前自动调用
if(name!=NULL)
{
free(name);
}
}
}
int mian()
{
A a(10);
}
//堆栈在a被声明时,在栈中,开辟存储a的内存,含有一个int大小的,和char*一个指针大小的空间,然后在堆中,开辟100个字节的空间,用Name指向它。
//但是析构的时候,只会自动清除栈的东西,所以一定要记得释放堆中的东西。
//栈是会被压的!!

拷贝构造函数:
class Test
{
//显式的拷贝构造函数,
Test(const Test &anthor)//const引用,就是不能改变anthor所代表的对象的任何东西。
{
//...
}
//=赋值操作符
void operator=(const Test &another)
{
//...
}
}
//和C#不同,C#是没有拷贝构造函数的。
//
int main(void){
Test t1;
Test t2(t1);
//这句话,如果=号操作没有重载,拷贝构造函数也没有重载,那么也相当于t2=t1;t1复制一份,给t2。
//就算Test没有传入参数为Test的构造函数,也能成功。能自动将成员值拷贝过去。
//因为有默认的拷贝构造函数
Test t3;
t3=t1;//这里就不是调用构造函数了,而是调用=操作函数
//
Test t4=func();//**这里也不会发生t4先构造,然后执行=操作,而是将func()返回的结果“转正”
//
t3=func();//**这里就会发生执行=操作了。而且func()返回的匿名对象会被析构掉。

}
******************************
//在C++中,struct和class没有明显差别。不同C#,class一定要new(手动开辟内存)出来。
//在classA a;这句中,已经调用了构造函数了。C#中,仅仅是声明一个空的引用变量a。
//在a=b;调用了操作符号=函数。C#中,则是引用变量a来引用b引用的对象,a和b都是引用同一个对象。
//所以C++中,对象的传参,参数返回(除非是返回堆中的对象引用),都是复制的操作,同时,复制的时候会调用默认的拷贝构造函数,出栈的时候,也会调析构函数。
//各种拷贝,析构,也是可以通过引用来优化的地方!
//这是C++和大多数语言的不同。
******************************
3. 深拷贝和浅拷贝
class Teacher
{
int m_id;
char*m_name;
public:
Teacher(int id,char *name)//这就算深拷贝了,浅拷贝的话,m_name的值和name是一样,指向同一块内存。
{
m_id=id;
int len = strlen(name);
m_name=(char*)malloc(len+1);//因为最后要以\0标志字符串结束,所以+1
strcpy(m_name,name);
}
//一般的浅拷贝构造函数
Teacher(const teacher&a)
{
mid=a.mid;
m_name=a.m_name;
}
//一般的浅拷贝构造函数
Teacher(const teacher&a)
{
//按Teacher(int id,char *name)来写。
}
~Teacher()
{
if(m_name!=NULL)
{
free(m_name);//记得释放堆上的内存
}
}
}
//*****free是不能重复对同一指针值(内存地址)执行两次的。
//*****所以,见到类中,有指针成员,一定要特别留意,先看析构函数,会不会有可能free同一个内存地址;然后考虑要不要深拷贝。
//*****浅拷贝风险,就是有指针成员时,析构时释放堆内存,可能会重复释放内存。
4. 构造函数的初始化列表
class B
{
public:
B(A &a1,A&a2,int m):m_a1(a1),m_a2(a2),m_m(m)//常量成员的赋值,只能放在初始化列表中
{
//如果这使用m_a1=a1,显然达不到调用A的构造函数的目的,只是调了A的=操作符函数
//如果这里写m_a1(a1),会语法错误,将m_a1当做了函数使用了
}
private:
A m_a1;
A m_a2;
int a=3;//很多编译器不支持这种写法,所以尽量要写到构造函数中
const int m_m;//只能赋值一次,而且不能const int m_m=3;因为有些编译器不支持
}
//在构造的时候,先执行A的构造两次,然后执行B构造函数体;在析构的时候,先析构m_a1和m_a2,然后再执行B析构的函数体。

5. 静态成员
void test()
{
int static a=10;//这句话只会在第一次执行的时候,给a赋值,因为a在运行初阶段就放在静态区了。
a++;
}


class A
{
public:
A()
{
//...
}
static int m_c;//也是在程序运行之初放在静态区的
}
**************************************************************
int AA::m_c=100;//在声明类的时候,就给静态变量赋初值了。
*记得#ifndef #endif

void funtion()
{
AA:m_c=100;//不用写成 int AA:m_c=100;这是在方法之外写的。
}

6. this指针
//对于一个类,只有非静态字段才属于这个对象(才在这个对象的内存中),static成员在静态区,而且方法也不在这个对象的内存区域中。
//那为什么a.getPrivateProperty()能获得a的私有成员呢,但是getPrivateProperty()方法不在a的内存区域内???
class Test
{
private:
int n1;
public:
Test(int i)
{
n1=i;
}
int getn1()
{
return n1;
//等价于return this->n1;
}

int getn2() const //加个const,防止这个方法内改变this所指向的内容,相当于const Test * const this(指向常量的常指针,相当于常引用了)
{
}

static void print()
{//...}
}
Test a(10);
a.getn1();
Test::Print();
通过编译后,变为:
struct Test
{
int n1;
};
void Test_initialize(struct Test *pThis,int i)
{
pThis->n1=i;
}
int Test_getn1(struct Test *pThis)
{
return pThis-<n1;
}
void Test_Print(){//.....}
Test a;
Test_initialize(&a,10);
Test_getn1(&a);
Test_Print();
//再次,在C++中,struct和class没有明显差别。

7. 返回对象本身,主要可以用于链式操作
class Test
{
private :
int a;
public:
&Text Add(Text &another)
{
this->a=this->a+another.a;
return *this;
}
}
//能达到每次连加都会改变自身,又不用复制。

8. 友元函数
class Point
{
private :
double x;
double y;
//
friend double PointDistance(const Point &p1,const Point &p2);
//也能friend double Calculater::PointDistance(const Point &p1,const Point &p2);
//Calculater类一定要在Point声明之前声明好
public:
Point(double x,double y)
{
this->x=x;
//...
}
double getX()
{
return this->x;
//以匿名的方式,返回x的复制值。
}
double getY(){ //...}
}
double PointDistance2(const Point &p1,const Point &p2)
{
//有getX(),getY()的操作,调用方法就有压栈出栈的过程,效率比p1.x低多了。
}

如果在类中声明有:
friend double PointDistance(const Point &p1,const Point &p2); 那么就可以p1.x,使用私有成员了。就像PointDistance是类中的成员函数一样。
**因为友元通常会破坏封装性,所以不推荐使用。

9. 声明。声明的作用,就是预先告知有这个东西,其地方要使用它,必须要有预先告知。比如:
double PointDistance2(const Point &p1,const Point &p2);
如果这个函数声明前,没有通过头文件,或者其他办法,获取到Point 的声明,那么编译不过的。
正确的办法:
class Point;//这个也是声明
double PointDistance2(const Point &p1,const Point &p2);
****这也是C#和C++不同的地方,C#是声明和实现放在一起,而且不分先后顺序。

10. 友元类
class B;
class A
{
friend class B;
priviate : int a;
}
class B
{
private : int b;
public:
B& addA(const A &a)
{
this->b= this->b+a.a;
return *this;
}
}
**因为友元通常会破坏封装性,所以不推荐使用。

11. 操作符重载
//
new和delete也是操作符,也能重载的
//
*通常会重载“=”号,用于深拷贝
*通常也会重载+-号,用于一些字段的加减
class Complex
{
private:
int a;
int b;
public:
Complex operator +(const Complex &b)
{
Complex c;
c.a=this->a+b.a;
c.b=this->b+b.a;
return c;
}
//如果写在外边的话Complex operator +(const Complex &a, const Complex &b); 调用也可以Complex c3= operator+(c1,c2);

Complex& operator +=(const Complex &b)
{
c.a=this->a+b.a;
c.b=this->b+b.a;
return this;
}

//后++运算,加const,防止拼命++下去
const Complex operator ++(int)//要有个int填坑
{
Complex temp(*this);
this.a++;
this.b++;
return temp;
}
//为了能cout << complex,但这里c1.operator<<(os);或者c1<<cout;太别扭
//所以只能写全局了
ostream & operator <<(ostream &os)
{
os<<this->a<<this->b;
return os;
}
}
ostream & operator <<(ostream &os,Complex &c);//这样就可以cout<<c了;


12. 任何传入类的字符串指针,都要考虑深拷贝和析构,否则如果外边释放了内存,那么使用会出错。

13. ***************************重写无参构造,=操作符必要的注意事项***************************
等号操作符注意事项:
class A
{
public:
char*Name;
A(const A &a)
{
this->name=new char[strlen(a.Name)];
if(this->name!=NULL){
strcpy(this->Name,a.Name);
}
}
//遇到有成员指针是开辟堆内存的,一定要重写=,重写无参构造,重写析构
A & operator=(const A &a)
{
if(this==*a) { return *a;}
//
if(this->name!=NULL)
{
delete[] this->name;
this->name = NULL;
}
//重新开辟
this->name=new char[strlen(a.Name)];
if(this->name!=NULL){
strcpy(this->Name,a.Name);
}
}
~A()
{
if(this->name==NULL)
{
delete[] this->name;
this->name = NULL;
}
}
}

14. []操作符重载,通常返回引用,因为a[i]应该既可以取,也可以修改才行。
char & operator[](const int &i)
{
return this->Name[i];
}

15. ()操作符重载
int main(void)
{
typeA a ;
a();//这里是由于操作符重载,所以可以这样
}

16. const引用和const函数
class A
{
public:
int getLenght1()//实际上会编译为 int getLenght(int *const this),const指针,指向不可变
{

}
//
int getLenght2() const //实际上为const int *const this,相当于const引用,指向内容不可变,指向不可变 , 相当于const A &a
{

}
}

// 不能通过const引用修改变量的值
void function(const A &a) //为了保护a不在方法中被改变
{
a.getLenght1(); //这里出错,用高安全的引用作为实参,调用低安全形参的函数。
a.getLenght2(); //这里才不会出错
}

猜你喜欢

转载自www.cnblogs.com/pylblog/p/9820751.html