bingc++类
Posted 鸟随二月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bingc++类相关的知识,希望对你有一定的参考价值。
目录标题
mutable
mutable int _day;
// 被mutable修饰的成员变量仍旧可以在const成员函数内部被修改
类
定义
C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
struct Student
void SetStudentInfo(const char* name, const char* gender, int age)
strcpy(_name, name);
strcpy(_gender, gender);
_age = age;
void PrintStudentInfo()
cout<<_name<<" "<<_gender<<" "<<_age<<endl;
char _name[20];
char _gender[3];
int _age;
;
class
方式一:
声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理
class am
int a1;
void chushihua(int a)
a1 = a;
; // 一定要注意后面的分号
#include<iostream>
#include<Windows.h>
using namespace std;
class am
int a1;
public:
void chushihua(int a)
a1 = a;
;
int main()
am a;
a.chushihua(2);
system("pause");
方式二:
声明放在.h文件中,类的定义放在.cpp文件中
封装︰将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
访问限定符说明
- struct定义的类:成员默认的访问权限是public ;calss定义的类:成员默认的访问权限private
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域
对象包含哪些成员
- 只包含数据,大小计算和结构体一样
- 一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类(大部分编译器中)。
this指针
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(指针对象是该类的指针类型)(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
特性
- this指针的类型:类类型T * const
- 只能在“成员函数”的内部使用
- this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
- this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递(其他参数通过参数压栈传递),不需要用户传递
打印this地址,存放地址,可否为空(重)
在类函数中写
Date* const &rthis = this;
cout <<&rthis <<endl;
以上得知:this地址存放在类函数中,即栈上
可为空,可能会崩溃(平时里a.b(),其实是将a的地址传递给b中的this)
编译器认识类:
1.编译器先识别类名
2.识别类中有那些成员变里
3.识别类中有那些成员函数
4.会对成员函数进行改写
默认成员函数
初始化和清理
构造函数主要完成初始化工作
析构函数主要完成清理工作
拷贝复制
拷贝构造是使用同类对象初始化创建对象
赋值重载主要是把一个对象赋值给另一个对象
取地址重载
主要是普通对象和const对象取地址,这两个很少会自己实现
构造函数
一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次(无返回值,可重载)。
#include<iostream>
#include<Windows.h>
using namespace std;
class Date
public:
// 1.无参构造函数
Date()
// 2.带参构造函数
Date(int year, int month, int day)
_year = year;
_month = month;
_day = day;
private:
int _year;
int _month;
int _day;
;
int main()
Date d1; // 调用无参构造函数
Date d2(2015, 1, 1); // 调用带参的构造函数
// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
Date d3();
system("pause");
return 0;
特性
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。
- 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成
- 无参的构造函数和全缺省的构造函数(全默认值构造)都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。
- C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如int/char…,自定义类型就是我们使用class/struct/union自己定义的类型,,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数
- C++11中针对内置类型成员平初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
析构函数
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作
特性
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值。
- 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
class SeqList
public :
SeqList (int capacity = 10)
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
~SeqList()
if (_pData)
free(_pData ); // 释放堆上的空间
_pData = NULL; // 将指针置为空
_capacity = 0;
_size = 0;
private :
int* _pData ;
size_t _size;
size_t _capacity;
;
5.关于编译器自动生成的析构函数,是否会完成一些事情呢?(和构造函数一样式的作用)
拷贝构造函数
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用
class Date
public:
Date(int year = 1900, int month = 1, int day = 1)
_year = year;
_month = month;
_day = day;
Date(const Date& d)
_year = d._year;
_month = d._month;
_day = d._day;
private:
int _year;
int _month;
int _day;
;
int main()
Date d1;
Date d2(d1);
return 0;
特性
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
- 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝(是什么拷什么),这种拷贝我们叫做浅拷贝,或者值拷贝。
- 考贝构造函数典型调用场景:
- 使用已存在对象创建新对象
- 函数参数类型为类类型对象
- 函数返回值类型为类类型对象
赋值运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
- 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
- 操作符有一个默认的形参this,限定为第一个形参
- .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
运算符重载
全局赋值运算符重载
#include <iostream>
using namespace std;
class Date
public:
Date(int year = 1900, int month = 1, int day = 1)
_year = year;
_month = month;
_day = day;
//private:
int _year;
int _month;
int _day;
;
bool operator==(const Date& d1, const Date& d2)
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
void Test ()
Date d1(2018, 9, 26);
Date d2(2018, 9, 27);
cout<<(d1 == d2)<<endl;
int main()
Test();
return 0;
作为成员函数的时候:
#include <iostream>
using namespace std;
class Date
public:
Date(int year = 1900, int month = 1, int day = 1)
_year = year;
_month = month;
_day = day;
//private:
int _year;
int _month;
int _day;
bool operator==( const Date& d2)
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
;
void Test ()
Date d1(2018, 9, 26);
Date d2(2018, 9, 27);
Date d3(d1);
cout<<(d1 == d3)<<endl;
int main()
Test();
return 0;
赋值运算符重载
一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝(浅拷贝),和拷贝构造函数一样
注意
- 浅拷贝:导致s1和s2析构时,一块内存空间释放两次导致程序崩溃
- 内存泄漏:赋值成功后,s2中之前的空间就没有机会释放了
- 必须成员函数
- 返回*this()
格式 - 参数类型: const T&,传递引用可以提高传参效率
- 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值。
- 检测是否自己给自己赋值
- 返回*this:要复合连续赋值的含义
class Date
public :
Date(int year = 1900, int month = 1, int day = 1)
_year = year;
_month = month;
_day = day;
Date (const Date& d)
_year = d._year;
_month = d._month;
_day = d._day;
Date& operator=(const Date& d)
if(this != &d)
_year = d._year;
_month = d._month;
_day = d._day;
retun *this;
//不返回d 1.d是const引用,2是当 d1=d2=d3;时,根据意义返回this
private:
int _year ;
int _month ;
int _day ;
;
int main()
Date d1;
Date d2(2018,10, 1);
d1 = d2;
return 0;
前置++后置++
#include <iostream>
using namespace std;
class Date
public:
Date(int year = 1900, int month = 1, int day = 1)
_year = year;
_month = month;
_day = day;
Date& operator=(const Date& d)
_year=d._year;
_day=d._day;
_month=d._month;
return *this;
int _year;
int _month;
int _day;
Date& operator++()
_day++;
return *this;
Date operator++(int)
Date d(*this);
_day++;
return d;
void print()
cout<<_year<<':'<<_month<<':'<<_day<<endl;
;
void Test ()
Date d1(2018, 9, 26);
Date d2=d1++;
d2.print();
d2=++d1;
d2.print();
int main()
Test();
return 0;
取地址重载&
#include <iostream>
using namespace std;
class Date
public:
Date(int year = 1900, int month = 1, int day = 1)
_year = year;
_month = month;
_day = day;
Date& operator=(const Date& d)
_year=d._year;
_day=d._day;
_month=d._month;
return *this;
int _year;
int _month;
int _day;
//前置++
Date& operator++()
_day++;
return *this;
//后置++
Date operator++(int)
Date d(*this);
_day++;
return d;
//取地址重载,没有时编译器自动生成
Date* operator&()
return this;
//当变量为const修饰不变时
//将const修饰的类成员函数称之为const成员函数,
//const修饰类成员函数,实际修饰该成员函数隐含的this指针,
//表明在该成员函数中不能对类的任何成员进行修改。
const Date* operator&()const
return this;
bool operator==( const Date& d2)
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
void print()
cout<<_year<<':'<<_month<<':'<<_day<<endl;
;
void Test ()
Date d1(2018, 9, 26);
Date d2=d1++;
d2.print();
d2=++d1;
d2.print();
cout<<&d2<<endl;
const Date d3(d1);
cout<<&d3<<end1;
int main()
Test();
return 0;
问题
- const对象可以调用非const成员函数吗?不可以
- 非const对象可以调用const成员函数吗? 可以的
- const成员函数内可以调用其它的非const成员函数吗?不可以
- 非const成员函数内可以调用其它的const成员函数吗?可以的
初始化列表
构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
#include <iostream>
using namespace std;
class Date
public:
Date(int year = 1900, int month = 1, int day = 1)
:_year(year),
_month(month),
_day(day)
Date(const Date& d)
:_year(d._year),
_month(d._month),
_day(d._day)
void print()
cout<<_year<<'-'<<_month<<'-'<<_day<<endl;
int _year;
int _month;
int _day;
;
void Test ()
Date d1(2018, 9, 26);
d1.print();
int main()
Test();
return 0;
注意
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量
- const成员变量
- 自定义类型成员(该类没有默认构造函数)
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义
以上是关于bingc++类的主要内容,如果未能解决你的问题,请参考以下文章