• OOP课程笔记1基本语法


    避免重复引用库

    func.h文件这么写

    #ifndef FUNC_H__
    #define FUNC_H__
    //正文
    #endif
    

    const关键字:

    const variables

    告诉编译器这个变量只读,必须要声明和初始化同时进行:

    const int N{100005};
    const int N = 100005;
    

    除非用extern关键字,涉及到一些编译的东西,暂时不用,不管。

    函数值传参时用const:

    void f(const int x) {
    }
    

    告诉编译器这个x在f里不能被改。

    函数引用|地址传参时用const:

    表示地址上的值不能被改。

    const放不同的位置还有不同效果:
    https://blog.csdn.net/cczk8138/article/details/37569223

    注意const int x和int const x的效果是完全一样的(const:常量)const int p 和int const p是不一样的,前者p不可变,p可变;后者p不可变,p可变。
    总之,在const之后的东西(*p或者p)是常量,不可变

    成员变量+const

    和外面的一个意思,只读。

    但因为某些原因,不能直接拿来开数组,加static就行。

    可以有限制地先声明再定义(不用加extern),限制即初始化时只能在初始化列表里,而不能在构造函数体时。

    比如这么写,在构造函数开始前的初始化列表写:

    class fred 
    { 
        const int size;
    public:
        fred();
    };
    fred::fred() : size{100}
    {
      // constructor body
    }
    

    暂不理解这么写有毛用。

    成员函数后+const

    参考:
    https://www.cnblogs.com/shiheyuanfang/p/13740816.html

    即告诉编译器这个函数是个对该类下变量只读的函数,除非定义成员变量前+mutable。

    因此const成员函数不能call 非const成员函数。

    class Test {
    
    int func() const;
    
    };
    
    int Test :: func() const {
        return 0;
    }
    

    as_const 和 const_cast

    暂不理解。

    fstream基础操作

    参考:
    https://blog.csdn.net/weixin_30800807/article/details/97188984
    https://blog.csdn.net/weixin_42587961/article/details/86677869

    简单来说fstream就是stream读入输出流的文件版本(平时用的cin本身是个ifstream,cout是个ofstream),关系就像scanf和fscanf那样,不过stream之流是C++的特性。

    fstream提供了三个类:

    1. ifstream
    2. ofstream
    3. fstream

    可以定义以下类型:

    • ios::in 为输入(读)而打开文件;
    • ios::out 为输出(写)而打开文件;
    • ios::ate 初始位置:文件尾;
    • ios::app 所有输出附加在文件末尾;
    • ios::trunc 如果文件已存在则先删除该文件;
    • ios::binary 二进制方式;
    • ios::nocreate:不建立文件,所以文件不存在时打开失败;
    • ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败;

    定义这些类型方法如下(在open时定义):

    //1
    ofstream file;
    file.open ("example.bin", ios::out | ios::app | ios::binary);
    //2
    ofstream file ("example.bin", ios::out | ios::app | ios::binary);
    

    ofstream, ifstream 和 fstream可以理解为默认带了不同的类型。

    1. ofstream 默认方式 ios::out | ios::trunc
    2. ifstream 默认方式 ios::in
    3. fstream 默认方式 ios::in | ios::out

    当然只要你定义了类型,这些默认的就会被覆盖。

    这三个类有下面这些基础成员函数调用:

    bool is_open(); //返回值表示文件是否被顺利打开
    bool bad(); //读写过程中是否出错(操作对象没有打开,写入的设备没有空间)
    bool fail(); //读写过程中是否出错(操作对象没有打开,写入的设备没有空间,格式错误--比如读入类型不匹配)
    bool eof(); //读文件到达文件末尾,返回true
    bool good(); //以上任何一个返回true,这个就返回false
    void close (); //关闭文件,但不加这个destrutor也会帮你自动释放。
    

    读入输出方法同cin,cout,把cin,cout换成对应的名称即可。

    缺省参数(Default arguments):

    void Simulation(int x, int y = 0, int z = 0);
    

    缺省的只能是末尾的连续一段变量(不然会二义),调用时可以省略末尾的连续一段变量,自动用缺省值代替。

    namespace

    namespace不要在头文件里打开

    static关键字

    static class members

    static member variables:

    1. 用于数组长度命名(代替enum)

    2. 用于类的对象计数

    注意(用于类的对象计数):
    类里所有的变量都是先声明,在创造对象是才会被定义。

    所以类里的静态成员变量必需在类外定义:

    int X::x=0;
    

    static member function:

    在类里,带有static关键字的函数只能访问也含有static的变量和函数。

    reference:

    即小学就学过的 & 的符号。

    void swap(int &x, int &y) {
      ...
    }
    

    它的作用就相当于别名,是为了替代麻烦的指针操作而存在的。

    &x的大小就是指针的大小。

    当然也可以直接定义这样的变量并初始化。
    注意必须定义的同时初始化,因为空的reference不行(空指针可以):

    int x;
    int &a{x};
    

    下面这么写就不行了:

    int x;
    int &a;
    a = x;
    

    也可以给函数个引用:

    string& larger(string &a, string &b) {
    	return a > b ? a : b;
    }
    
    int main() {
    	string a{"123"};
    	string b{"321"};
    	larger(a, b) = "567";
    	cout << a << endl;
    	cout << b << endl;
    }
    
    

    reference原则:

    永远不要引用传给一个将来会被删除的变量(栈变量)。

    用&加速:

    我们知道函数传参时,如果参数是个很大的类,这个会被复制一遍,浪费时间,所以如果没有什么对类的修改,可以加&。

    以防万一,可以const & 一起用。

    用auto &遍历数组并修改:

    for (auto &t : temperatures) {
      t = 0;
    }
    

    Copy constructor

    即在复制或者传参时的一个构造函数。

    格式:

    X(const X&boj){...}
    

    如果没有写,编译器会帮忙合成一个复制构造函数,这个复制构造函数原理是位复制(即全部内容照搬)。

    当有指针时,在delete时可能会重复,从而暴毙。

    设计原则上需避免copy constructor。

    可以一个空的在private里或者用delete关键字:

    A(const A& a) = delete;
    

    lvalue, rvalue, move constructor

    lvalue:

    能放等号左边的东西。

    rvalue:

    能放等号右边的东西
    比如说一个函数:

    inf f(){...}
    

    就只是右值不是左值。

    这个值可能在用一次后就被销毁了。

    const and non-const reference

    non-const reference 只接受左值。
    const reference 接受左值和右值。

    比如:

    int &r = f();
    

    是不行的

    const int &r = f();
    

    是可以的。

    rvalue reference

    关键字 &&
    其只接受将要销毁的有值。

    比如:

    int lval = 2;             // lval is an lvalue 
    int f() {...}             //f() is rvalue
    int&& ref_rval = 5;       // OK: binds to an rvalue 5
    int&& ref_rval2 = lval;   // Error: rvalue ref cannot bind to lvalue
    int&& ref_rval3 = lval+5; // OK: binds to an rvalue  
    int&& ref_rval4 = f();    // OK: binds to an rvalue
    

    move constructor

    为了提高效率,如果在x=y这样一个操作后y的值没有用了

    可以用x=std::move(y)来完成,此时y成为一个rvalue reference。

    当然,当一个函数返回值就是该类时,自动是rvalue reference。

    针对这样右值引用的复制构造函数是不一样的,我们称为move constructor。

    语法:

    X(X && obj){
    }
    

    有指针时,可以把 && obj 里的指针复制后再设为nullptr。

    构造函数或者析构函数被定义时,编译器就不会合成移动构造函数。

    protected 对象:

    private < protected < public

    protected 对象可以给派生类使用,但不能在外面使用。

    namehiding

    在派生类中,如果出现了f这个函数,那么基类中所有叫f的函数都会被hide起来。

    如果想在外面用基类中的叫f的函数,可以在加using BaseClass :: f。

    这样会expose BaseClass 中所有叫f的函数。

    如果只想expose一个,可以用forwarding functions。

    即在派生类手动写一个相同的函数,返回基类中函数的值。

    也可以把不想用重新声明为private。

    比如:

    class A {
    public:
    	void f(int x) {
    		printf("int int\n");
    	};
    	void f(string x) {
    		printf("string\n");
    	}
    };
    
    class B : public A {
    public:
    	using A :: f;
    	void f() {
    		printf("Null\n");
    	}
    private:
    	void f(int x);
    };
    
    class C : public A {
    public:
    	void f() {
    		printf("Null\n");
    	}
    	void f(string x) {
    		return A :: f(x);
    	}
    };
    
    
    

    enum

    格式为:

    enum type {
      a, b, c=100
    }
    

    其中a,b,c的值默认是0,1,2,...

    可以自己赋值,值必须是整数。

    然后每个元素就与对应的值建立了联系,b 等价于 (type) 1

    他们本身也对应一个整数,可以拿来用。

    比如

    int x = c;
    if(x == c) {
    
    }
    int arr[c];
    

    但不能被一个整数直接赋值。

    type并不完全像class或者struct中的类名,他可以是一个枚举变量的代指,语法为:

    void f(type) {}
    f(a);
    

    friend:

    参考:
    https://blog.csdn.net/weixin_39951988/article/details/87087025

    友元函数:

    一个类的私有成员在类外是无法被访问的。

    但是有些类外函数频繁需要访问类的成员,这时可以用友元函数。

    友元函数不是成员函数,而是全局函数。

    关于一个类的友元函数需要在类里声明,前面加上friend。

    定义可以在里或者外(在外可以不用加friend),该函数可以访问private成员,但是由于没有this指针,所以需要在参数里传对象的引用来访问。

    class ClassA {
    private:
    	int x;
    public:
    	friend void Add(ClassA &a, int y);
    };
    
    void Add(ClassA &a, int y) {
    	a.x += y;
    }
    
    

    类做友元:

    class ClassB {
    	friend ClassA;
    };
    

    ClassB中可以访问ClassA的对象的private成员。

    继承中的屁事:

    继承中的构造函数:

    假设一个派生类D继承了多个基类A,B,C,

    class D : public A, public B, public C {
    
    };
    

    那么在生成D的对象时,

    那么会先调用A,B,C的构造函数。

    D的构造函数就是为A,B,C的构造函数传递参数的通道,必须在D的构造函数的初始化列表上来构造A,B,C,如果没写,就是用默认的没参数的那个。

    注意,构造函数调用的顺序就是(A,B,C),即继承时的顺序,和初始化列表中的无关。

    A,B,C也是派生类的话,递归考虑即可。

    继承中的析构函数:

    调用顺序与构造函数相反。

    先调用派生类的,再调用派生类里内嵌对象的,最后基类。

    继承中的拷贝构造函数:

    派生类的拷贝构造函数肯定是要考虑一下基类的,不然的基类就默认用标准构造函数生成了。

    一般可以直接用upcasting解决这个问题,假设基类已经写了拷贝构造函数,那么派生类只需要:

    Child(const Child& c) : Parent{c}, i{c.i}, m{c.m} {
    }
    

    上面的m是Child中的内嵌对象。

    继承中的移动构造函数:

    同理:

    Child(Child&& c) : Parent{move(c)}, i{c.i}, m{move(c.m)} {
        cout << "Child(Child&&)\n";
    }
    

    final:

    final修饰一个类时,该类不能被继承

    class A final {
    };
    

    final修饰虚成员函数:
    让其不能被继承:

    virtual void f() final {
    
    }
    
  • 相关阅读:
    [转]C#汉字转拼音的源码
    [转]C# DES 加密/解密类库,支持文件和中文/UNICODE字符,返回BASE64编码字符串
    48瓶子,48种性格
    “识谎”36计
    巧克力有益智商 经常吃可提高大脑计算能力
    调用方未由服务进行身份验证
    揭秘人体24小时使用手册
    [转]C#实现人民币金额小写转大写的代码
    转一篇绝对详细的手工构造PE文件教程
    bat 查找某个进程的ID号
  • 原文地址:https://www.cnblogs.com/coldchair/p/16006496.html
Copyright © 2020-2023  润新知