6.21 c++

1深复制和浅复制

在使用一个对象对另一个对象初始化或赋值时,复制构造函数能对对象的属性进行复制。但如果存在指针成员,则指针的值会被复制,导致两个对象的指针成员指向同一块内存,在释放的时候对象的时候,会重复释放,导致程序崩溃。若对象包含指针成员变量,则需要手动的编写拷贝构造函数实现深拷贝,调用编译器的内部默认的拷贝构造函数则只能实现浅拷贝操作。

深拷贝:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

class student{
public:
	student(int age,char *name){
		this->age=age;
		this->name=new char[10];
		strcpy(this->name,name);
	}
	student(const student& s){
		this->age=s.age;
		this->name=new char[10];
		strcpy(this->name,name);
	}
	~student(){
		delete this->name;
	}
    void show(){
        printf("Student[addr=%x, age=%d, age.addr=%x, name=%s, name.addr=%x, name.value=%x]\n", this, age, &age, name, &name, name);
    }
private:
	int age;
	char* name;
};
int main(){
	student s1(20,"小明");
	s1.show();
	student s2=s1;
	s2.show();
	return 0;
}

调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生,如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。

2.静态成员变量:

有时候我们希望在多个对象之间共享数据,对象a改变了某份数据后对象b可以检测到。
典型例子就是 计数
例子:以student为例,如果我们想知道班级中共多少名学生,就可以设置一份共享的变量,每次创建对象时让该变量加1。
没有在类外初始化的static成员变量是不能使用的

#include<iostream>
using namespace std;

class student
{
public:
	student(char* name,int age,float score):m_name(name),m_age(age),m_score(score){
		m_total++;
	}
	void show(){
		cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"(当前共有"<<m_total<<"名学生)"<<endl;
	}
private:
	int m_age;
	float m_score;
	char* m_name;
	static int m_total;
};
int student::m_total=0;
int main(){
	student stu((char*)"小张",15,90);
	stu.show();
	(new student((char*)"小明",15,90))->show();
	(new student((char*)"小王",16,92))->show();
	(new student((char*)"小李",12,88))->show();
	(new student((char*)"小华",18,92))->show();
}

输出:
小张的年龄是15,成绩是90(当前共有1名学生)
小明的年龄是15,成绩是90(当前共有2名学生)
小王的年龄是16,成绩是92(当前共有3名学生)
小李的年龄是12,成绩是88(当前共有4名学生)
小华的年龄是18,成绩是92(当前共有5名学生)

3.静态成员函数

普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员可以调用静态成员函数。

#include<iostream>
using namespace std;

class student
{
public:
	student(char* name,int age,float score):m_name(name),m_age(age),m_score(score)
	{
		m_total++;
		m_point+=score;
	}
	void show(){
		cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
	}
	static int gettotal()//静态成员函数
	{
	return m_total;
	}
	static float getpoint()//静态成员函数
	{
	return m_point;
	}
private:
	static int m_total;
	static float m_point;
	char *m_name;
	int m_age;
	float m_score;

};

int student::m_total=0;
float student::m_point=0.0;

int main(){
	(new student((char*)"小明",15,90))->show();
	(new student((char*)"小王",16,92))->show();
	(new student((char*)"小李",12,88))->show();
	(new student((char*)"小华",18,92))->show();
	int total=student::gettotal();
	float points=student::getpoint();
	cout<<"当前共有"<<total<<"名学生,总成绩"<<points<<",平均分是"<<points/total<<endl;
	return 0;
}

输出:
小明的年龄是15,成绩是90
小王的年龄是16,成绩是92
小李的年龄是12,成绩是88
小华的年龄是18,成绩是92
当前共有4名学生,总成绩362,平均分是90.5

4.const修饰成员函数

const成员函数可以使用类中的所有成员变量,但是不能修改他们的值,这种措施主要还是为了保护数据而设置的。const成员的函数也称为常成员函数。

#include<iostream>
using namespace std;

class student
{
public:
	student(char* name,int age,float score):m_name(name),m_age(age),m_score(score){}
	void show()
	{
		cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
	}
	char* getname() const;
	int getage() const;
	float getscore() const;
private:
	char* m_name;
	int m_age;
	float m_score;
};

char*  student::getname()const
{
	return m_name;
}

int student::getage()const
{
	return m_age;
}

float student::getscore()const
{
	return m_score;
}

getname(),getage(),getscore()三个函数的功能都很简单,仅仅是为了获取成员变量的值,没有任何修改成员变量的企图,所以我们加了const 限制,这是一种保险的做法,同时也便得语义更加明显。
需要注意的是,必须在成员函数的声明和定义处同时加上const关键字。char * getname() const和char *getname()是两个不同的函数原型,如果只在一个地方加const会导致声明和定义处的函数原型发生冲突。

5.前向引用申明

class B;//没有这句会报错
class A{
public:
	void f(B b);
};
class B{
public:
	void f(A a);
};

经过类的前向引用申明,可以声明该类的对象引用或指针。但是在提供一个类的完整定义之前,不能定义该类的对象,也不能在内联函数中使用该类的对象。

6.位域

有些信息在存储时,并不需要占用一个完整的字节,而只需要占用一个或几个二进制位。
长度最小的char和bool在内存中都占据一个字节的空间。c++允许在类中声明位域。位域是一种允许将类中的多个数据成员打包,从而使不同的成员可以共享相同的字节的机制。在类中的定义位域的方式为:
数据类型说明符 成员名 :位数;
位数指定一个位域所占用的二进制位数。使用位域:
(1)C++并未规定打包的具体方式,因此不同的编译器会有不同的方式,不同编译器下,包含位域的类所占用的空间也不同。
(2)只有bool, char,int, enum的成员才能够被定义为位域。
(3)位域虽然节省了内存空间,但由于打包和解包过程中需要耗费额外的操作,所以运行时间很可能会增加。

#include<iostream>
using namespace std;

enum Level {FRESHMAN, SOPHOMORE, JUNIOR, SENIOR};
enum Grade {A, B, C, D};

class Student {
public:
    Student (unsigned number, Level level, Grade grade) : number(number), level(level), grade(grade) {}
private:
    unsigned number : 27;
    Level level : 2;   //类型一定要为unsigned,否则如果最高位为1,会被解释为负数
    Grade grade : 2;
};

int main(){
    Student s (123456578, SOPHOMORE, B);
    cout << sizeof(s) << endl;
}

输出 4

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值