Cpp笔记——class 类

定义类时,classstruct关键字唯一的区别在于默认访问权限。

构造函数

默认构造函数

只有当类没有声明任何构造函数的情况下,才会自动生成默认构造函数,否则需要手动构造。

但是如果构造函数的参数都设置了默认值,那么相当于定义了默认构造函数

class Base {
public:
    int i;
    int j;
	// A不会报错,因为相当于有无参默认构造函数了
    Base(int v = 11) : i(v) {}
    // A报错,因为声明了构造函数后,不会自动生成默认构造函数
    Base(int v) : i(v) {}

};

class A {
public:
    Base base;
    int v;
	// base会调用Base的默认构造函数
    A(int i) : v(i) {}
};

int main() {
    A a(1);
    cout<<a.base.i<<endl;
}

=default

因为若定义含参构造函数,那么我们需要有默认构造函数,但是这个时候不会再自动生成(原因如上)。所以我们使用BaseClass()=default来让编译器自动生成默认构造函数。

类外构造函数

可以在类里面声明构造函数,类外实现构造函数

class Base {
public:
    int v = 1;
    string s;
    Base();
};
Base::Base(){
    v = 11;
    s = "??";
}

构造函数初始化列表

class Base {
private:
    int val;
    string s;
    const int a;
    int &ri;
    const int b = 1;
public:
	// const对象和引用必须被显式初始化
    Base(int v, string S) : val(v), s(S), a(1), ri(val) {}

    // 与上面效果等价,但是没有使用列表初始化
	Base(int v, string S){
		val = v;
		s = S;
		// error:const对象和引用只可以被显式初始化
		a = 1;
		ri = val;
	}
};

注意
使用列表初始化的话,可以直接显式的初始化成员,如上面第一个。
没有使用列表初始化,那么:在构造函数体之前,成员函数先执行默认初始化,然后在函数体内再执行赋值操作。
const对象和引用必须被显式初始化,构造函数一开始执行,初始化就完成了,所以只可以通过参数列表或者类成员默认值来初始化

成员初始化顺序

与在函数初始值列表中出现的顺序无关,因为其不限定初始化的具体执行顺序。其成员的初始化顺序与他们在类中定义的出现顺序一致。
在这里插入图片描述

class Base {
public:
    int i;
    int j;

//    Base(int v) : j(v), i(j) {}
// 	  cout值 1 0. 因为i定义在j之前

    Base(int v) : i(v), j(i) {}
    // cout值 1 1
};

int main() {
    Base base(1);
    cout<<base.j<<" "<<base.i;
}

委托构造函数,cpp11新标准

其实就是一个类的构造函数可以调用其他构造函数
在这里插入图片描述

隐式类型转换

下面是例子,有对应的构造函数的时候,可以进行隐式转化。

class Base {
public:
    int i;
    string s;
	
    Base() : s("NULL"), i(0) {}

    Base(int v = 11) : i(v), s("NULL") {}

    Base(string S = "NULL") : s(S), i(0) {}

    Base(string S, int v) : s(S), i(v) {}
	// 定义为void show(Base& p) const 的话就不可以隐式转换了
    void show(Base p) const {
        cout << s << "-" << i << " AND " << p.s << "-" << p.i << endl;
    }
    explicit Base(pair<int, int> p){
		// ...
		// 此构造函数不支持隐式转换,因为有explicit,除非用static_cast<Base>()
	}
};


int main() {
    string s = "HELLO";
    Base base("HAHA");
    base.show(s); // HAHA-0 AND HELLO-0
    int p = 111;
    base.show(p); // HAHA-0 AND NULL-111
	
	// error,只允许一步类类型转换,下面需要先吧"Hello"转为string,再转为Base,所以错误
	// base.show("Hello");
	base.show(string("Hello"));
}

禁止隐式转换,关键字explicit

聚合类

在这里插入图片描述

类的静态成员

类的静态成员存在于任何对象之外,每个静态成员,每个类只有一个对应的静态成员对象,静态成员函数不与任何对象绑定,因而其没有this指针,不能声明为const。
但是使用类的静态成员时,我们可以有如下访问方式

// 假设cnt和plus()是静态变量和静态成员
    int a = Base::cnt;
    Base::plus();
    
    base.plus();
    
    Base &r = base;
    r.plus();
    
    Base*pp = &base;
    pp->plus();

在这里插入图片描述

初始化

只有常量可以在类内初始化

class Base{
    static const int cnt = 1;
    static constexpr int cnt2 = 1;
    static int v;
};

友元

类可
以允许其他类或者函数访问其非公成员:将其他类或者函数声明为其友元
友元声明只能出现在类定义的内部。友元不是类的内部成员也不受它所在区域的访问控制级别限制。
友元没有传递性

class D{
	// 先定义
	void f();
};

class Base {
// 一般统一声明友元
    friend ostream &printBase(ostream &os, Base &base);
	// 友元类
    friend class FB;
	// 将某个类的函数设为友元
	// 要求:
	// 首先定义D类,其中声明f函数,但是不能定义它。在f函数使用Base的成员之前必须先声明Base
	// 定义Base,并声明友元
	// 定义D::f,这时候才可以访问Base的成员
	friend void D::f();
	
private:
    int v = 1;
    string s;
public:
    Base() : v(100), s("Hello World !") {}
};

class FB {
    Base base;
public:
    FB() = default;

    FB(Base base1) : base(base1) {
        cout << "Friend Class From Base!" << endl;
        cout << base.v << " " << base.s << endl;
    }
};

ostream &printBase(ostream &os, Base &base) {
    os << base.s << " " << base.v << endl;
    return os;
}

void D::f(){
	// visit Base
}

int main() {
    Base base;
    printBase(cout, base);
    FB fb(base);
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

类的其他feature

//
// Created by 13653 on 2022/4/20.
//
#include<iostream>
#include<vector>

using namespace std;


class Screen {
public:
    using pos = string::size_type;

    Screen() = default;

    Screen(pos ht, pos wd, char c = ' ') : height(ht), width(wd), contents(ht * wd, c) {}

    char get() const {
        return contents[cur]; // 隐式内联,在类中定义的函数一般会隐式自动内联
    }

    // 重载了get
    inline char get(pos ht, pos wd) const; // 显式内联

    Screen &move(pos r, pos c); // 可以在之后设置为内联private:
    // size_type 是无符号类型,可以表示任意string对象的长度

    void some_member() const {
        // mutable永远不会是const的
        ++access_ctr;
    }

    Screen &set(pos r, pos c, char v) {
        contents[r * width + c] = v;
        return *this;
    }

    Screen &set(char v) {
        contents[cur] = v;
        return *this;
    }

    // 基于const的重载
//    Screen display(ostream& os) const{
//        doDisplay(os);
//        return *this;
//    }
    const Screen &display(ostream &os) const {
        cout << "const 版本" << endl;
        // const函数只能调用const函数
        doDisplay(os);
        return *this;
    }

    Screen &display(ostream &os) {
        cout << "非 const 版本" << endl;
        doDisplay(os);
        return *this;
    }

private:
    pos height = 0, width = 0, cur = 0;
    mutable pos access_ctr;
    string contents;

    void doDisplay(ostream &os) const {
        os << contents << endl;
    }
};

// 在函数定义处指定内联,别忘了加Screen:: 指定类名
inline Screen &Screen::move(pos r, pos c) {
    pos row = r * width;
    cur = row + c;
    return *this;
}

// 在类的内部声明成inline
char Screen::get(pos ht, pos wd) const {
    pos row = ht * width;
    return contents[row + wd];
}

class Window_mgr {
private:
    // 类内初始化只可以通过两种方式:
    // I:使用等号,如 int a = 1;
    // II: 花括号
    vector<Screen> screens{Screen(2, 3, ' ')};
    int a{3};
};

int main() {
    Screen myScreen(2, 2, 'A');
    const Screen blank(1, 1, 'B');
    myScreen.display(cout).set('Q').display(cout);
    blank.display(cout);
    /*在类中我们可以基于const重载,因为有时为了链式调用,我们会返回类的引用对象
     * 但是const函数不可以返回非const的引用对象。我们可以针对const来重载函数
     * 因为非常量版的函数对于常量对象来说是不可引用的,所以一个常量对象只可以调用const成员函数
     * 非常量对象可以调用const和非const函数,但是优先匹配非const函数
     * 
     */
}

类的前向声明

可以先被声明但不被定义,这个时候他是不完全类型(因为不清楚其成员)。这个时候我们可以定义指向这个类型的指针或引用,也可以将其作为参数或者返回类型。
但是我们不可以创建其对象,因为它还没被定义,编译器无法为其分配空间等一系列操作。

class Node {
    int v;
    // 是可以的,因为是指针,这个时候不必初始化
    Node *left, *right;
};

class Node2 {
    int v;
    // 不行,因为只有当类被全部完成后才算被定义,所以类不可以指向自己
//    Node2 left, right;
};

// X有Y的指针,Y有X的对象
class Y;

class X {
    Y *y;
};

class Y {
    X x;
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值