#include <iostream>
#include <assert.h>
using std::cout;
using std::endl;
// 类的声明放在ClassName.h 中,
// 类的定义放在ClassName.cpp 中!
// 开源项目一般将类的声明和定义放在一起ClassName.hpp 中!
class Person {
public:
void showInfo(); // .h 中声明!
private:
char* _name;
char* _sex;
int _age;
};
// .cpp 中定义!
void Person::showInfo() {
cout << _name << "-" << _sex << "-" << _age << endl;
}
// 类的访问限定:
// public: 修饰的成员在类外可以直接访问!
// private和protected: 修饰的成员在类外不能直接访问!
// class 的默认访问权限是private,struct是public;
// 类的实例化!
// 类只是一个模型一样的东西,限定了类有那些成员,定义出一个类并没有分配实际的内存空间来存储它!
// 一个类可以实例化出多个对象,实例化的对象占实际的物理空间,存储成员变量!
// 对象是如何存储的?
// 类的变量的值可以是不一样的,但是成员函数的代码是相同的!
class A1 {
public:
void f1() {};
private:
int _a;
};
class A2 {
public:
void f2() {};
};
class A3 {
};
#if 0
// 可以发现,对象存储时,只将类的成员变量每个成员单独一份,代码是共享的!
int main() {
A1 a1;
A2 a2;
A3 a3;
cout << sizeof(a1) <<" "<< sizeof(a2) <<" "<< sizeof(a3) << endl;
return 0;
}
#endif
// 结构体内存对齐规则:
// 第一个成员在与结构体偏移量为 0 的地址处!
// 其他成员变量要对齐到某个数字(对齐数 = 编译器默认的堆区数与该成员的大小的较小值!)的整数倍的地址处!
// 结构体的总大小为: 最大对齐数的整数倍(min(所有变量类型最大者,编译器指定对齐数))
// 嵌套大小,嵌套的结构体算作一个成员!
// this 指针:
class Date {
public:
void Display() {
cout << _year << "-" << _month << "-" << _day << endl;
}
void SetDate(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
// C++ 编译器给每个非静态成员函数增加了一个隐藏的this指针,让该指针指向当前对象,在函数体中所有成员变量的操作都是通过该指针去访问的
// 只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成!
#if 0
int main() {
Date d1, d2;
d1.SetDate(2022, 7, 6);
d2.SetDate(2022, 7, 7);
d1.Display();
d2.Display();
return 0;
}
#endif
// this 指针的特性:
// 1. this 指针的类型: 类类型* const
// 2. 只能在成员函数的内部使用!
// 3. this 指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参. 所以对象中不存储this指针!
// 4. this 指针是成员函数第一个隐含的指针参数,一般情况下由编译器通过ecx寄存器自动传递,不需要用户传递!
class A {
public:
void PrintA() {
cout << _a << endl;
}
void Show() {
cout << "Show()" << endl;
}
private:
int _a;
};
#if 0
//. this 指针的类型: 类类型* const
int main() {
Date* p = NULL;
//p->PrintA();
//p->Show();
return 0;
}
#endif
//默认成员函数:
// 初始化和清理: 构造函数,析构函数!
// 拷贝复制: 使用同类对象初始化创建新对象! 赋值重载主要是把一个对象赋值给另一个对象!
// 取地址重载: 主要是普通对象和 const 对象取地址!
// 1. 构造函数:
// 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值
// 并且在对象的生命周期只调用一次!
// 如果一个类没有写构造函数,编译器会自动生成一个无参构造函数!
// 一旦用户显示定义了构造函数,则不会再生成默认的无参构造!
class Time {
public:
Time() {
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Datr{
private:
// 基本类型,默认赋值!
int _year;
int _month;
int _day;
// 自定义类型,调用其无参构造赋值!
Time _t;
};
#if 0
int mian() {
Datr d;
return 0;
}
#endif
// 2. 析构函数
// 与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是编译器完成的!
// 而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作!
// 特征:
// (1) 析构函数名是在类名前加上~
// (2) 无参数无返回值!
// (3) 一个类有且之后一个析构函数。若未显示定义,系统会默认生成析构函数!
// (4) 对象生命周期结束时,C++编译器系统会自动调用析构函数!
typedef int DataType;
class SeqList {
public:
SeqList(int capacity = 10) :_capacity(capacity) {
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
}
~SeqList() {
cout << "~SeqList()" << endl;
free(_pData); // 释放堆上的空间!
_pData = nullptr;
_capacity = 0;
_size = 0;
}
private:
int * _pData;
size_t _size;
size_t _capacity;
};
#if 0
int main() {
SeqList sq;
return 0;
}
#endif
// 3. 拷贝构造函数
// 构造函数只有一个形参,且该形参时本类类型对象的引用(一般是 const的),在用已存在的类类型对象创建新对象时由编译器自动调用!
// 特征:
// 拷贝构造函数时构造函数的一个重载形式!
// 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用!
// 如果没有显示定义拷贝函数系统会自动生成拷贝构造函数,默认的拷贝构造函数对象按内存存储字节完成拷贝!(浅拷贝!)
class Data {
public:
Data(int year = 2022,int month = 1, int day = 1) :_year(year), _month(month), _day(day) {}
Data(const Data& data) {
_year = data._year;
_month = data._month;
_day = data._day;
}
private:
int _year;
int _month;
int _day;
};
// 运算符重载:
// C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,
// 函数名字以及参数列表,其返回值类型与参数列表与普通函数类似!
// 函数名字为: 关键字operator后面接需要重载的运算符符号
// 函数原型: 返回值类型 operator操作符(参数列表)
// 内置类型的操作符,其含义不能改变;
// . * :: sizeof ?:不能重载!
// 全局operator==
class DateA {
public:
DateA(int year = 1900, int month = 1, int day = 1) :_year(year), _month(month), _day(day) {}
bool operator==(const DateA& d) {
return _day == d._day && _month == d._month && _year == d._year;
}
DateA& operator=(const DateA& d) {
_year = d._year;
_day = d._day;
_month = d._month;
}
int _year;
int _month;
int _day;
};
void Test() {
DateA d1(1,2,3);
DateA d2(2,4,5);
cout << (d1 == d2) << endl;
return;
}
#if 0
int main() {
Test();
return 0;
}
#endif
// 7. const 成员
// const 修饰尘缘函数: const 修饰的成员函数称为const成员函数,实际修饰该成员函数隐含的this指针,表明该成员函数中不能对类的任何成员进行修改!
class AnoutConst {
public:
void show1() {
cout << "show()" << endl;
}
void call(int val) {
_val = val;
show();
}
void show()const {
cout << "show() const" << endl;
// call(3); const 成员函数不能调用非const成员函数!
}
private:
int _val;
};
// 取地址以及const 取地址操作符重载!
class AboutCon {
public:
AboutCon* operator&() {
return this;
}
const AboutCon* operator&() const {
return this;
}
private:
int _val;
};
// 构造方法的初始化列表:
// 1. 每个成员变量在初始化列表中只能出现一次!
// 2. 类中包含一下成员必须放在初始化列表位置进行初始化!
// (1) 引用成员变量
// (2) const 成员变量
// (3) 自定义类型成员变量(该类没有默认构造函数!)
// 3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量一定会使用初始化列表初始化!
// 4. 成员在类中声明次序就是其在初始化列表中的初始化顺序,与其在列表中的先后次序无关!
// 对于单个参数的构造函数,构造函数有类型装欢的作用!
// 使用 explict 修饰构造函数,将会禁止单构造函数隐式转换!
// static 成员:
// 声明为static的类成员,称为类的静态成员.
// 静态成员变量一定要在类的外部进行初始化!
class B {
public:
B() {
_count++;
}
B(const B& b) {
++_count;
}
static int GetCount() {
return _count;
}
private:
static int _count;
};
int B::_count = 0;
// 静态成员为所有类对象所共享,不属于某个具体的实例!
// 静态成员变量必须在类外定义,定义时不添加static关键字!
// 静态成员没有隐藏的this指针,不能访问任何非静态成员!
// 静态成员也有访问级别!
// 友元: 突破封装的一种便利!
// 友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但是需要在类的内部声明,声明时要加上friend关键字!
// 友元函数不能用const修饰!
// 友元类: 关系是单项的,A声明B是friend那么B就能访问A的全部,但是A不能访问B的全部!
// 内部类:
// 如过类B定义在类A的内部,那么类B就叫做内部类,但是B是独立的,不依赖A存在而存在!
// 内部类就是外部类的友元类,但是外部类不是内部类的友元类!
// 内部类可以直接访问外部类中的static、枚举成员、不需要外部类的对象/类名!
// sizeof(外部类) = 外部类 ,和内部类没有任何关系!
自学C++ day02 (OOP)
最新推荐文章于 2024-09-05 23:31:31 发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)