学习目标:
本文主要记录学习C++ 时的需要注意的地方,基础的我就跳过了。学习内容:
- 类
C++中类的定义方式跟Java差不多,如下:
public:
int age;
int getAge() const;
void setAge(int age);
//构造函数
person() {
this->age = 0;
cout << "init age: 0"<<endl;
}
person(int age) {
this->age = age;
cout << "init age: " << age <<endl;
}
//拷贝函数
person(const person &p){
this->age = p.age;
cout << "person : copy function " << this->age <<endl;
}
//析构函数
~person() {
cout << "free" << endl;
}
};
int person::getAge() const {
return this->age;
}
void person::setAge(int age) {
this->age = age;
}
当我们使用的时候可以如下使用
person p;
//下面的几行都没错,先注释,是为了说明个问题。
//person p(10);
//p->setAge(10);
输出结果:
init
free
咦?我们只声明了 person,为啥会输出东西呢?这是因为你的思想还停留在Java的逻辑里,在C++中,当我们用类声明时,它的初始化函数也会被调用,在当前函数生命周期结束后,或调用这个类的析构函数进行内存的释放。
还有一种不同于Java的类的初始化方式,如下所示:
//person p(10);
//与上面的代码执行等效
person p = 10;
c++ 中还有析构函数和拷贝函数。
析构函数: 相当于我们在android 中经常会在生命周期结束的是否解绑广播,释放一些不必要的内存(析构函数有C++系统自动调用,我们可以在析构函数里写一些释放内存的逻辑)。
拷贝函数: 这个跟Java中的拷贝差不多,当我们作为函数参数和赋值以及返回值时都会触发这个拷贝函数
,像下面这样
这里是浅拷贝:拷贝浅层,不拷贝深层(深层使用引用的方式)
int main() {
person p;
//函数传递发生拷贝
test(p);
return 0;
}
void test(person p) {
//赋值时发生拷贝
person p1 = p;
}
//作为返回值的时候发生拷贝
person test() {
person p;
return p;
}
参数列表
//参数列表
person(int age,char *name):age(age),name(name){
}
这样的话 我们就不用像java一样繁琐的写好多代码,初始化参数了。
一个对象有多大?是这个对象中最大元素的整数倍。
举个栗子。
//sizeof(pserson) = 1
class person{
}
//sizeof(person1) = 8
class person1{
int i = 0;
char ch;
}
详细解释:
因为这个对象里有int类型(4个字节),char类型(1个字节),理论上共5个字节。
但由于C++有内存对齐的策略
,所以占用字节数的大小是这个对象里成员变量中占用的最大字节数的整数倍
,从例子看出int
是我们这个对象里的最大的元素 — 4个字节,所以这个对象的占用空间只可能是4,8,12,16…,我们选一个最合适的也就是8,也就是8>5,能放下,所以综上所述,这个对象占用8个字节。
下面我们自己手敲一个Java中的Arraylist
//ArrayList.h
#include <iostream>
using namespace std;
//模板(泛型) 多种基本数据类型存放适配
//ps:存放类对象还没试过,但应该问题不大
template<typename T>
class ArrayList {
private:
//默认长度
int DEFAULT_SIZE = 20;
//长度
int length = 0;
//实际长度
int actual_size = 0;
//存放内容
T *content;
public:
ArrayList() {
this->length = DEFAULT_SIZE;
this->actual_size = 0;
this->content = (T *) malloc(sizeof(T) * this->length);
}
explicit ArrayList(int capacity) {
this->length = capacity;
this->actual_size = 0;
this->content = (T *) malloc(sizeof(T) * this->length);
}
//析构函数
~ArrayList() {
if (this->content != NULL) {
delete[] this->content;
this->content = NULL;
}
}
//拷贝构造函数
ArrayList(const ArrayList<T> &temp) {
this->length = temp.length;
this->actual_size = temp.actual_size;
this->content = (T *) malloc(temp.length * sizeof(T));
copy(temp.content, temp.content + temp.length, this->content);
}
/**
* 从末尾添加元素
* @param val 元素
*/
void add(T val);
/**
* 在指定的位置添加元素
* @param pos 当前位置
* @param val 元素
*/
void add(int pos, T val);
/**
* 获取元素
* @param pos 元素下标
* @return 元素
*/
T get(int pos);
/**
* 删除元素
* @param pos 元素下标
*/
void remove(int pos);
/**
* 获取实际长度
* @return 实际长度
*/
int getLength();
/**
* 判断是否为空
* @return true:空
*/
bool isEmpty();
/**
* 扩容当前存放元素的空间
*/
void resize();
/**
* 格式化输出
*/
void toString();
};
template<class T>
void ArrayList<T>::add(T val) {
add(actual_size, val);
}
template<class T>
void ArrayList<T>::add(int pos, T val) {
if (pos < 0 || pos > this->length) {
return;
}
if (this->actual_size >= this->length * 0.75) {
resize();
}
this->content[pos] = val;
actual_size++;
}
template<class T>
T ArrayList<T>::get(int pos) {
if (pos < 0 || pos > length) {
return NULL;
}
return this->content[pos];
}
template<class T>
void ArrayList<T>::remove(int pos) {
if (pos < 0 || pos > length) {
return;
}
for (int i = pos; i < length - 1; i++) {
content[i] = content[i + 1];
}
actual_size--;
}
template<class T>
int ArrayList<T>::getLength() {
return actual_size;
}
template<class T>
bool ArrayList<T>::isEmpty() {
return actual_size == 0;
}
template<class T>
void ArrayList<T>::resize() {
this->length *= 2;
T *p = (T *) malloc(this->length * sizeof(T));
copy(this->content, this->content + this->length, p);
delete[] this->content;
this->content = p;
}
template<class T>
void ArrayList<T>::toString() {
cout << "[ ";
for (int i = 0; i < actual_size; i++) {
if (i == actual_size - 1) {
cout << content[i];
continue;
}
cout << content[i] << ",";
}
cout << " ]" << endl;
}
文中的copy
函数是STL库函数,大家可以百度使用方法,大概意思把start~last位置的元素复制到result地址开始的地方。
接下来就可以像Java中一样使用了,如下所示:
#include <iostream>
#include "ArrayList.h"
using namespace std;
int main() {
ArrayList<int> arrayList(10);
for(int i=0;i<100;i++){
arrayList.add(i);
}
arrayList.toString();
return 0;
}
在手敲一个Java中经常使用的单例,注意看注释,一定要在类外显示声明类中的静态成员变量。
class CrashHandler {
public:
static CrashHandler *getInstance() {
if (instance == nullptr) {
instance = new CrashHandler;
}
return instance;
}
private:
//只是声明,未分配内存
static CrashHandler *instance;
static int a;
//私有化构造函数
CrashHandler() {};
//私有化拷贝函数
CrashHandler(const CrashHandler &){};
};
//显示声明static变量,并且为它分配内存
//这句话一定要写,不然直接GG
CrashHandler* CrashHandler::instance = NULL;
int main() {
CrashHandler *p = CrashHandler::getInstance();
return 0;
}
- 操作符重载
这个是用来对一些运算符,比如new * & + = *
等等运算符,进行重写,可以达到自己的目的,这个可以在用来优化开辟时的内存等等意向不到的效果,由于太菜,只能举个小例子—智能指针充充数。
template <class classType>
class SmartPointer{
public:
SmartPointer(classType * ptr){
this->pointer = ptr;
}
~SmartPointer(){
if(this->pointer!= nullptr){
delete this->pointer;
}
}
classType* operator->(){
return this->pointer;
}
classType& operator*(){
return *(this->pointer);
}
private:
classType *pointer;
};
这个跟STL标准模板库里的shared_ptr
功能差不多,在main函数中而可以如下调用:
SmartPointer<FeMale> female = SmartPointer<FeMale>(new FeMale);
- 虚函数
先举个栗子,不要用虚函数话会有什么问题?上代码
class animal{
public:
void eat(){
cout<<"animal eat"<<endl;
}
void drink(){
cout<<"animal drink"<<endl;
}
};
class cat :public animal{
public:
void eat(){
cout<<"cat eat"<<endl;
}
void drink(){
cout<<"cat drink"<<endl;
}
};
int main() {
animal * p = new cat();
p->eat();
return 0;
}
输出结果 :
animal eat
按照Java的思维,应该会输出子类的方法,但是不行,但是我们改成下面就可以了。
class animal{
public:
virtual void eat(){
cout<<"animal eat"<<endl;
}
void drink(){
cout<<"animal drink"<<endl;
}
};
class cat :public animal{
public:
virtual void eat(){
cout<<"cat eat"<<endl;
}
void drink(){
cout<<"cat eat"<<endl;
}
};
int main() {
animal * p = new cat();
p->eat();
return 0;
}
输出结果 :
cat eat
从结果看到,我们仅仅加了virtual
字段,
我们实现了我们需要的效果(多态),这个就是虚函数实现的效果,接下来在画一下类继承里的函数会发生什么变化
分析: 首先说明f()
方法是虚函数,图上没标明,然后类继承的时候,子类实现了父类的虚函数即覆盖父类的方法。子类在继承的时候,子类的继承的父类方法和成员变量并不是子类的指向父类的方法,而是把父类的方法copy过来了,为什么可以这么讲,我们来验证一下。
class animal{
public:
int num;
virtual void eat(){
cout<<"animal eat"<<endl;
}
};
class cat :public animal{
public:
};
int main() {
cout<<sizeof(cat);
return 0;
}
输出结果
16
可以看到我们这个cat
并没有任何成员变量和方法,但是测量大小却是16
,这就验证了,我们子类在继承的时候会把父类的成员变量和函数都copy一份,存在自己这儿。
持续更新中。。。