C++ class 基础学习分享总结

class类

概念,定义

类核心思想是 
    有哪些数据,如何表示数据,如何使用操作数据 以及对外暴露的接口(用户或计算机如何交互)
    类设计尽量将共有接口和实现细节分开
与结构的区别 
    结构默认都是 public 
    类默认都是 private  
    其他的确实差不多 但是一般结构只用于纯粹的数据描述对象 
接口 
    是一个共享框架,供两个系统交互时使用,不能直接操作类但是可以通过接口间接控制类 
抽象 
    将一个对象抽象成具有哪些属性和方法 一般在公有部分public
封装 
    将实现细节放在一起,并与抽象分开,成为封装一般在私有部分,或将数据封装到一起 (private) 
public 
    公共访问 
private 默认  没有这个表示的 都是私有
    数据部分一般封装起来
    私有访问 仅该类中可以使用
    私有函数 处理不属于公有接口的实现细节(不用关注是如何实现的放到私有里)
protected
    保护访问 
    本类和子类中可以访问
        子类可以直接访问保护属性 但是不能访问基类的私有属性 
    缺点是 派生类中可以直接修改,使之变成了公有访问
    所以能用私有用私有,
    保护函数会比较有用,可以让派生类使用不能公共访问的内部函数

构造函数

定义 
	创建对象时,自动对它进行初始化
	没有返回类型 也不用声明void 
	构造函数中参数名不能和成员名相同

默认构造函数

如果没有显示构造函数,则自动创建默认构造函数
 class C1 {
     public: 
         C1(); // 默认可以不用写
 }
 如果参数都有默认值,则也是默认构造函数 
 class C1 {
     public: 
         C1(int a = 1,int b = 2); // 参数都有默认值的也是默认构造函数
 }
 默认构造函数只有一个 否则 
     C1 c(); 这样构造时会产生二义性
 若有显示构造,则默认构造函数必有一个 否则
     C1 c(); 这样构造时会报错 除非不这样用
 使用 
     C1 c;
     C1 c();
     C1 c = C1();
     C1* c = new C1();
     以上调用的都是默认构造函数

传参/显示构造函数

class C1 {
   public: 
        C1(int a); // 显示构造
        C1(int a,int b); // 支持函数重载
        C1(int a,int b int c); // 切记函数参数签名需要不同
}
// 使用 
C1 c(1);
C1 c = C1(1,2);
C1* c = new C1(1,2,3);
// 以上根据不同传参 调用不同显示构造函数
特殊使用 
    隐式转换
        一个参数的构造函数 允许使用赋值语句进行初始化
        C1 c = 1;     // 隐式转换 该特性可被关闭  
        C1 c = C1(1); // 显示转换
        关闭 隐式转换 特性
            public: 
                explicit C1(int a); 
        此时 C1 c = 1; 将会报错
        显示转换 不会被关闭
            C1 c = C1(1); 还是可以的 
构造函数中 成员初始化列表
    常量是只在初始化时赋值 所以类构造时 就需要初始化常量 
    这种语法 只有构造函数可以使用  这种初始化的效率最高 
    且必须用这种格式初始化 非静态const数据,引用数据 
    数据成员顺序跟出现在类定义中的顺序最好相同 初始化顺序是声明定义顺序,而不是初始化列表顺序
    C1::C1(int a):age(a){}
        age(a) 表示 将age 赋值为a   
    C1::C1():age(1),sex(1){} // 同时允许这样初始化数据 

复制构造函数

定义 
	将一个对象复制到新创建的对象中(按值传递时也会调用此)
	直接创建对象和先创建临时对象 再赋给别的对象,两种实现方式可能根据编译器不同
	如果类中包含了new初始化指针成员,则应定义一个复制构造函数 
	如果没有显示定义会提供默认复制构造函数 默认的复制行为是 逐个复制非静态成员的值
// 格式 
class C1 {
    C1(const C1& cc); // 注意 参数是该类的引用传值
}
//使用 
C1 c1 = C1(); // 定义一个原始值

C1 c2 = c1;  // 方式1 
C1 c2(c1);   // 方式2
C1 c2 = C1(c1); // 方式3
C1* p_c2 = new C1(c1) // 方式4 
注意点  见下面示例
	浅复制
	    小心默认复制 默认复制是按值复制,也是浅复制
	    a.str = b.str; 此时复制的是b.str的指针 并不是新内存地址 
	    然后 a 中调用析构函数 则b.str 将会是乱码 
	    然后 b 中调用析构函数 又会发生二次释放内存
	    造成内存管理不善的错误 
	    所以对象的复制中存在指针复制时 需要谨慎处理 
	深复制 
	    原理是 开辟新内存 存储值 
class C1 {
public:
    C1(char* p_str){ 
        // 构造函数中使用new 创建内存指针 
        p_str_ = new char[strlen(p_str) + 1];
        strcpy(p_str_,p_str);
    };
    // 复制构造函数 如果不提供则调用默认的 可以自行尝试效果
    C1(const C1& ff){
        // 重新new 一个新地址出来存放复制值的副本
        p_str_ = new char[strlen(ff.p_str_) + 1];
        strcpy(p_str_,ff.p_str_);
    };
    ~C1(){
        // 析构时 delete
        delete [] p_str_;
    }
    char* getStr() const { return p_str_; }
private:
    char* p_str_;
}

int main(){
	C1 f1("hahaha");
	
	C1 f2(f1);
	std::cout << f2.getStr() << std::endl;
	return 0;
}

移动构造函数 todo

析构函数

定义 
	对象过期时,会自动调用该对象的析构函数,用来释放内存。
	    构造函数中使用了new 分配内存 则析构函数中必须用delete释放掉 
	默认会有隐式析构函数 
	没有参数和返回值 
	什么时候调用是由编译器决定,通常不用显示调用
声明显示析构函数 最后每个类都有显示析构函数
	class C1 {
	    public:
	        ~C1(){}
	}

运算符重载

定义 
    根据操作符和参数类型确定采用哪种运算符重载函数
    运算符必须是有效的C++运算符,暂不支持自定义运算符 例如@#¥ 
    支持[] 符号 其他的 就是+-*/ = & || ! ~ > < () [] 
限制规则 
    使用运算符时,不能违反原来的运算符语法规则,不能将-减法重载成+法 
    不能修改运算符优先级 
    不能创建新运算符 
    不能重载的运算符 
        sizeof  
        . .* 等 
格式 
   operator符号(argument)
定义原型  
    int operator+(const C1 & c);
声明实现 
    int C1::operator+(const C1& c){}
使用
    int val = c1 + c2; 
        // 实际则为 int val = c1.operator+(c2) 
        // 左侧为对象调用 右侧为传递参数
    int val = c1 + c2 + c3;
        则转换为 int val = c1.operator+(c2 + c3) 
                int val = c1.operator+(c2.operator+(c3)) 
// ... 省略其他代码
// 自定义类 + 运算符的操作
void operator+ (const char* str){ // 原型中直接定义并实现
    char* oldp_str = new char(strlen(p_str_) + 1);
    strcpy(oldp_str,p_str_);
    delete p_str_;
    p_str_ = new char(strlen(oldp_str) + strlen(str) + 1);
    strcpy(p_str_,oldp_str);
    strcpy(p_str_ + strlen(oldp_str),str);
    delete [] oldp_str;
}
// ... 省略其他代码
C1 f1 = "haha";
f1 + "aaaa";
// ... 省略其他代码
operator 特殊使用
类转其他值 
	定义 
	    必须是类方法 
	    不能指定返回类型
	    不能有参数 
	格式 
	    operator typeName();  虽然没有返回类型 但是也要有return typeName;
	示例 
	    定义原型  operator int();  如果转成int 则用此方法 
	    使用  int num = (int) cc;
	        将调用cc上的 转换函数 
	    其他说明 
	        int arr[];
	        arr[cc] 这种会调用隐式转换 如果需要去掉 原型加 explicit 
	            期望 cc 转成int 当做下标 
赋值运算符 类赋值 
	涉及内存指针时 赋值运算也存在浅复制问题,需要手动定义
	原型
	    C1& C1::operator=(const C1&);
	被调用时机
	    将已有对象赋给另一个对象时
	    C1 c1;
	    C2 c2;
	    c2 = c1; // 此时是赋值 
	自定义赋值时 
	    先检查是否是自身赋值 this == &arg; 判断地址是否一样
	    需要先delete 旧的内存 在new 新内存;
	示例 见下
C1& operator= (C1& ff){
    if(this == &ff){
        return *this;
    }
    delete [] p_str_;
    p_str_ = new char(strlen(ff.p_str_) + 1);
    strcpy(p_str_,ff.p_str_);
    return *this;
}

成员函数

静态函数 
	如果是在public中定义 则可以通过 类目::静态函数 调用 
	静态函数中 只能使用静态数据,不能访问成员数据 
	public: 
	    static int getI();
	使用 
	    int val = C1::getI();
    
const 成员函数 
    一种场景
        const C1 c1 = C1();
        c1.show(); // show方法如不是const 则此处错误 因为不确定是否会修改对象
    解决
        原型 void show() const;
        声明 void C1::show() const {}
    定义 
        确保方法不会修改对象 
        只要类方法不修改调用对象,就应将其声明 const
内联函数 
	定义位于声明中的函数都将自动成为内联函数
	或在定义时增加inline关键字 

成员属性

静态成员 
	在原型(头文件)中定义 
	    不能在原型中 初始化静态成员的值 
	    如果静态成员是整型或枚举型const,则可以在类声明中初始化
	在实现(源文件)中初始化 
	    int StrBad::num_str = 0;
	如果类中的静态数据,在对象初始化时会发生变化,则需要特殊处理 
	    提供一个显示复制构造函数处理静态数据
const成员 
	可以在类描述中初始化,但是类描述中不能直接使用 见下面类作用域
静态 const 成员 
	可以在类描述中初始化,可以在类描述中直接使用 见下面类作用域
enum成员 
	可以在类描述中初始化,可以在类描述中直接使用 见下面类作用域

this指针

定义 
	   每个成员函数都包含this指针,指向调用对象
	   this指针指向用来调用成员函数的对象
示例 介绍this指针的作用和用法 
	原型 const C1 & isTop(const C1 & c) const; 
	    希望作用为 本身对象与另一个对象判断哪个最高则返回
	    隐式调用自身,显示调用其他对象,并返回其中一个对象 
	声明 const C1 & isTop(const C1 & c) const{
	    if(c.topNum > topNum){  // 此处 topNum 其实是 this->topNum 的简写
	        return c;
	    } else {
	        return *this; // 此处引出this 
	    }
	}

对象数组

C1 list[4];
如上声明 会初始化4个C1对象,自动调用默认构造函数 
所以即使没有显示赋值 也可以使用 list[0].show() 方法

类作用域 特殊注意点

类中定义的属性,函数等其作用域为整个类中 
描述对象时只是描述了对象的构造,属性,方法等,并没有创建内存
所以描述对象和创建对象是两回事  
class C1 {
    private: 
        // 错误注意点1 
        const int num = 30; // 错误的定义 描述时不能初始化成员 (旧版本 新版本支持)
        double list[num]; // 描述时num没有初始化 所以此处不对 解决办法如下

        enum {Months = 12};
        double list[Months]; // 此处可以 1

        static const int num = 12; // 此处可以 2
        double list[num];
}
此处可以 1 
    声明枚举不会创建类属性,只是一个符号,编译器会将该符合替换成值 12
此处可以 2
    常量与静态变量存储在一起,将不会存储在对象中,详细见静态成员
状态成员  外部可以这样使用 
    C1::Months

枚举类

enum e1 {One,Two}
enum e2 {One,Two}
    普通定义枚举 这样编译会不通过,因为存在重名问题 

解决办法为 枚举类
    enum class e1 {One,Two}
    enum class e2 {One,Two}
    enum class : short e3 {A1,A2} 指定类型 如果没有指定,则会根据编译器系统
        :short 指定为short类型 
使用 
    e1 val = e1::One;
    e2 val2 = e2::One;
区别 
    普通枚举会自动转为整型 
    枚举类不会有自动转换 

完整示例

class file1 {
public:
    file1(){};  // 默认构造函数
    file1(char* p_str){
        p_str_ = new char[strlen(p_str) + 1];
        strcpy(p_str_,p_str);
    };
    file1(int a) : num(a){}; // 显示构造函数
    file1(int a,int b){}; // 显示构造函数
    file1(int a,int b,int c){}; // 显示构造函数 支持函数重载

    file1(const file1& ff){
        p_str_ = new char[strlen(ff.p_str_) + 1];
        strcpy(p_str_,ff.p_str_);
    };
    ~file1(){
        delete [] p_str_;
    }
    char* getStr() const { return p_str_; }
    int getNum() const { return num; };


    void operator+ (const char* str){
        char* oldp_str = new char(strlen(p_str_) + 1);
        strcpy(oldp_str,p_str_);
        delete p_str_;
        p_str_ = new char(strlen(oldp_str) + strlen(str) + 1);
        strcpy(p_str_,oldp_str);
        strcpy(p_str_ + strlen(oldp_str),str);
        delete [] oldp_str;
    }
    file1& operator= (file1& ff){
        if(this == &ff){
            return *this;
        }
        delete [] p_str_;
        p_str_ = new char(strlen(ff.p_str_) + 1);
        strcpy(p_str_,ff.p_str_);
        return *this;
    }

    operator std::string(){

        return std::string(p_str_);
    }

protected:

private:
    char* p_str_;
    int num;
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hamburgerV

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值