C++杂记

数组

#include <iostream>

int main() {

    int a[5]; //在当前栈帧上为其分配内存

    int* b = new int[5]; //在堆中为其分配空间
    //数组的大小最好用一变量手工记录,而不是用sizeof(a)/sizeof(int) 因为这种方式有可能出错

    delete[] b; // 释放堆中的数组空间
    exit(0);
}

指针

#include <iostream>

int main() {
    char* buf = new char[8];
    memset(buf, 0, 8);

    delete[] buf;
    std::cin.get();
}

引用

#include <iostream>
#define LOG(MSG)  std::cout<<MSG<<std::endl;

//这种写法是错误的,因为会创建出一个新的变量val
void wrong_increment(int val) {
    val++;
}

//参数类型为引用int&,不创建新变量,只是引用已经存在的变量
void right_increment(int& val) {
    val++;
}

int main() {
    int a = 4;
    int& ref = a;
    //引用只是一个已经存在变量的别名,不创建新的变量,不占实际的堆栈空间
    //一旦引用了某变量,则不能将其重新绑定到别的变量上
    //可将其视为对指针解引用的简化语法,它只能指代变量本身(值),因此无法对其进行换绑(即不能将其指向另一地址)
    LOG(a);


    wrong_increment(a);

    right_increment(ref);
    LOG(a);

    std::cin.get();
}

类与结构体

/*C++中的可见性只有三个级别:
private 只有该类与其友元类可以访问
protected 只有该类与其子类可以访问
public 无限制

*/
#include <iostream>

//在C++中struct和class没有太大差别
//默认struct的成员是public的,
//C++ struct 中也可以有成员方法,但通常用struct表示数据的聚合

struct Vector {
    //public:
    int x;
    int y;
};

// class的默认访问等级是private
class Point {
public:
    /*C++与java不同,其类的成员变量不会有默认的初始化值,因此必须进行初始化(初值),
    这一操作往往在构造函数中完成*/

    //为了让某个类不能被实例化,隐藏构造函数的方式有两种:
    //1. 可以删除默认构造函数 用 =delete;
    //2. 将其用private修饰
    Point() {

    }
    Point(int x) = delete;
    ~Point() {
        std::cout << "this is a destoryed func " << std::endl;
    }


    int x;
    int y;

    void setx(int newx) {
        x = newx;
    }
};


/*实现一个Log类

1. 先从如何使用它出发来设计API:
Log log;
log.setLevel(LOGLEVEL_WARNING);
log.warn("message");


*/

//m_前缀表示私有的成员变量

class Log {

public:
    enum level {
        LOGLEVEL_INFO = 0,
        LOGLEVEL_WARNING,
        LOGLEVEL_ERROR
    };
private:
    level m_loglevel;
public:
    void setLevel(level level) { //将成员变量和传参的类型限制为枚举类型
        this->m_loglevel = level;
    }

    void info(const char* msg) {
        if (this->m_loglevel <= LOGLEVEL_INFO) {
            std::cout << "[info] " << msg << std::endl;
        }
    }
    void warn(const char* msg) {
        if (this->m_loglevel <= LOGLEVEL_WARNING) {
            std::cout << "[warn] " << msg << std::endl;
        }
    }
    void error(const char* msg) {
        if (this->m_loglevel <= LOGLEVEL_ERROR) {
            std::cout << "[error] " << msg << std::endl;
        }
    }

};

int main() {

    Log log;
    log.setLevel(Log::LOGLEVEL_ERROR);
    Point p;

    log.info("hello");
    log.warn("fucking");
    log.error("world");

    std::cin.get();//在此语句之后才会执行Point的析构函数
}

静态static

#include <iostream>

//1. 在class内使用static,表示所有类的实例共享这一成员,共享同一份内存
//静态方法无法访问非静态变量


/*2. 在class之外使用static,表示该变量只在该文件内可见
每个cpp文件为一个翻译单元,被是static修饰的全局变量仅在该文件中可见
两个文件中被static修饰同名全局变量不会发生定义冲突。
当想在当前文件(翻译单元)使用其它文件中公开的全局变量时,
要在当前翻译单元用extern声明它

一个常用做法是在头文件中用static修饰变量,当该头文件被多个cpp文件引入时会自动地将这些变量私有化


*/

/* 3.函数体中的static变量
函数体中的static变量相当于可见范围被限制在函数体中的全局变量,其生存周期几乎为整个进程的周期
被static修饰声明的变量仅仅在第一次会被初始化并赋值,再次进入该函数时这句初始化将不起实质作用,仅仅引用已经存在变量的值


*/

class Entity {

public:
    int x, y;
    static int share;
    void print() {
        std::cout << x << "," << y << std::endl;
    }
};


void inc() {
    static int i = 1;
    std::cout << i++ << std::endl;
}

int Entity::share = 100;//静态成员变量需要在外部定义,并且不能在未定义的情况下直接使用

int main() {

    Entity e = { 3,6 };

    Entity::share = 100;

    e.print();

    inc();
    inc();
    inc();

    std::cin.get();
    exit(0);
}

继承

#include <iostream>

//继承减少了代码重复 
class Entity {// 实体类
public:
    Entity() {
        x = 0;
        y = 0;
        std::cout << "initing..." << std::endl;

    }
    float x, y;
    void move(float a, float b) {
        x += a;
        y += b;
        std::cout << x << "," << y << std::endl;

    }
private:
    int passwd;

};

class Player :public Entity {//玩家类将包含Entity所有public级别的成员
public:
    int age;
    char* name;
};


int main() {
    Player p;//调用自己的构造函数时,将会先调用父类的构造函数
    p.move(2, 3);

    exit(0);

}

虚函数


#include<iostream>
#include<string>
/*多态的实现需要依赖于父类中的虚函数,
被virtual标识过的函数会生成V表,以便根据不同的子类对象来调用相应的方法
与virtual 经常配合使用的是override,虽然不是必须的,但它可以强迫子类中被override修饰的函数一定在父类中出现过
*/
/*
纯虚函数是在C++中创建接口的手段, 通过 virtual  func_name()=0 将父类中一个没有实现的成员函数设为纯虚函数
强迫其子类若想实例化则必须实现的函数,并且同时提供多态的特点
*/

//经常把满足某一性质、具有某种特定功能抽象成一个接口。如:Runnable Printable


class PrintNameable {

    virtual void printName() = 0;
};

class Entity : public PrintNameable {

public:
    int x, y;
    void move(int a, int b) {
        x += a;
        y += b;
    }

    //  virtual log() = 0;


    virtual void print() {
        std::cout << "hello Entity!" << std::endl;
    }

};


class Player :public Entity {

public:

    void printName() override {
        std::cout << "Player" << std::endl;
    }
    void setName(const std::string& name) {
        this->name = name;
    }
    void print() override {
        std::cout << "hello " << this->name << std::endl;
    }

    // void log() override {
    //     std::cout << "log!!" << std::endl;
    // }
private:
    std::string name;

};


int main() {
    //Entity e;
    Entity* p = new Player();
    ((Player*)p)->setName("sun");//此处因为setName是子类特有的方法,必须强转为子类指针才能调用
    //e.print();
    p->print();
    exit(0);
}

初始化列表

#include <iostream>
//在任何地方都用初始化列表来对成员变量进行初始化

class Info{

    public:
        Info(){
            std::cout<< "info is initing .."<< std::endl;
        }
        Info(const std::string& s){
            std::cout<< "info is initing by "<< s << std::endl;
        }

};

//初始化列表要求列出的成员变量的顺序必须是按照类中的声明顺序。

//并且,初始化列表是必须的。即便某个成员变量没有在初始化列表中进行初始化,编译器也会在每个构造函数的开始处调用该成员的默认构造函数:
//因此若不用初始化列表,而在函数体内进行初始化,则会造成对同一成员变量的重复初始化。
class Person{

    private:
        Info info;
        int age;
    public:

        Person(const std::string& s){ // 对成员info的首次初始化:调用Info的默认构造函数
            info = Info(s); //对成员info的第二次初始化
        }
};

int main(){
    Person *p = new Person("PP");
    return 0;
}

const

#include <iostream>

//1. 修饰普通变量
//2. 修饰指针,修饰引用
//3. 修饰成员方法

class Entity{

    private:
        int m_x,m_y;

         mutable int debug_var; //显式被mutable标记的变量可以在const修饰的成员方法中被改变。
    public:
        int getX() const { //不能在被const修饰的方法中修改类的成员变量
            debug_var++;
            return m_x;
        }

};
//成员函数后的const:
// 这个标志让我们能在传入参数为常对象时(eg: const myClass& e )
// 使得调用其不改变自身值的成员函数是合法的

void printX(const Entity& e ){
    std::cout<< e.getX() << std::endl;

}

int main(int argc, char **argv)
{

    const int MAX_SIZE = 100; //常量

    int *p1 = new int;
    *p1 = 200;

    p1 = (int *)&MAX_SIZE; //绕过const的一种方式:将其赋值给一个非const指针
    *p1 = 30;
    std::cout << *p1 << std::endl;

    //const 在*之前:
    const int *p2 = new int;
    //*p2 = 20; //不能修改其值
    p2 = &MAX_SIZE; // 但能改变其指向

    //const 在*之后
    int *const p3 = new int;
    *p3 = 99; //能改变其指向的值
    //p3 = &MAX_SIZE;//但不能改变其指向
    
    Entity e ;
    printX(e);
    return 0;
}

mutable

#include<iostream>

/* 两种mutable的常用方式:
 *
 * 1. 使得变量可以在const修饰的方法中可以改变其值
 *
 * 2. lambda表达式中 使得按值传入的参数的副本可以被修改
 */


class Entity{
    private:
        int x,y;
        mutable int debug_count = 0; //第一种 mutable 的用法。

    public:
        Entity(){

            x = 0;
            y = 0;
        }
        const int& getx() const {

            debug_count++;
            return this->x;
        }
};

int main(){
    const Entity e; //声明为const的对象,其构造函数一定要完成对其成员变量的初始化。
    std::cout << e.getx()<< std::endl;

    // mutable的第二种用法
    int x = 9;
    auto f = [=]() mutable{
        x++;
        std::cout<<x<< std::endl;
    };
    f();
    return 0;
}

更多的字符类型

#include <iostream>
#include <cstdlib>

int main()
{
    const wchar_t *wstr = L"long char";      //字节数不确定
    const char16_t *str16 = u"2 bytes char"; //每个字符固定2字节
    const char32_t *str32 = U"4 bytes char"; //每个字符固定4字节
    //===============================================
    //有一个方法可以让两个字符串字面量直接相加:使用字符串字面量对象:string literals
    using namespace std::string_literals;
    std::string res_str = "hello "s + "world!"s; //在每个字符串后加上"s" ,将其转换为字面量对象

    //前缀R+括号():直接按照字面量的格式输出字符串
    const char *example =
        R"(line1
line2
line3)";

    std::cout << example << std::endl;

    exit(0);
}

字符串

#include <iostream>
#include <string>
//#define NOTDEBUG

void printString(const std::string &str);
int main()
{
    const char *name = "sun"; //在GCC中 不加const的char*字符串默认为一个常量不能修改其值( 存储在常量区 )
    //name[2] = 's';//会报错  可以将字符串声明为数组或是在堆上为它分配空间
    std::cout << name << std::endl;

    std::string name2 = "salted";
    //对string进行追加操作。要对string类型变量使用+= ,而不是将两个字符串字面量相加,因为他们本质上是指针
    name2 += " hello!";
    std::cout << name2 << " " << name2.size() << std::endl; // <string> 头文件中对<<操作符进行了重载!!

    if (name2.find("llo") != std::string::npos) //npos 表示不存在
    {
        printString("found it!");
    }
    exit(0);
}

//关于将字符串传给函数:
//通常不会将形参写为 void printString(std::string str){} 因为这样会导致将字符串拷贝一份

//通常采用引用的方式进行传递,若此函数中的字符串时只读的,只需要在前面加上const修饰引用即可
void printString(const std::string &str)
{
#ifndef NOTDEBUG
    std::cout << str << std::endl;
#else
    //do nothing
#endif
}

线程

#include "tools.hpp"
#include <thread>

bool isfinshed = false;
void dosomething() {
    using namespace std::chrono_literals;
    std::cout << "current thread ID " << std::this_thread::get_id() << std::endl;
    while (!isfinshed) {
        print_lib();
        std::this_thread::sleep_for(1s);
    }

}

int main() {
#if 1
    std::cout << "main thread ID " << std::this_thread::get_id() << std::endl;
#endif
    std::thread worker(dosomething);

    std::cin.get();
    isfinshed = true;

    worker.join();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值