类和对象(Week 3)

内联成员函数和重载成员函数

内联成员函数

  • inline + 成员函数
  • 整个函数题出现在类定义内部
class B{
inline void func1(); //方式1
void func2() //方式2
{ };
}; void B::func1(){}

成员函数的重载及参数缺省

  • 重载成员函数
  • 成员函数——带缺省参数
#include<iostream>
using namespace std;
class Location{
private:
intx,y;
public:
void init(int x=0,int y=0); //存在2个缺省参数
void valueX(int val) {x = val;}//1
int valueX(){return x;}//2
//1和2是重载函数
}
  • 使用缺省参数要注意避免有函数重载时的二义性

构造函数

基本概念

  • 成员函数的一种

    • 名字与类名相同,可以有参数,不能有返回值(void也不行)
    • 作用时对对象进行初始化,如给成员变量赋初值
    • 如果定义类的时候没写构造函数,则编译器生成一个默认的无参数的构造函数
      • 默认构造函数无参数,无任何操作
    • 如果定义了构造函数,则编译器不生成磨人的无参数的构造函数
    • 对象生成时构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数
    • 一个类可以有多个构造函数
  • 为什么需要构造函数
    1. 执行必要初始化工作,不需要专门再写初始化函数
    2. 有时对象没被初始化就使用,会导致程序出错
class Complex{
private:
double real,imgae;
public:
void Set(double r, double i);
};//编译器自动生成默认的构造函数 Complex c1;//默认的构造函数被调用
Complex* pc = new Complex;//默认的构造函数被调用
class Complex{
private:
double real,imgae;
public:
Complex(double r, double i = 0);
};
Complex::Complex(double r, double i){
real = r;
imag = i;
} Complex c1; //error,缺少构造函数的参数
Complex *pc = new Complex;//error,没有参数
Complex c1(2); //OK
Complex c1(2,4),c2(3,5);
Complex *pc = new Complex(3,4);
  • 可以有多个构造函数,参数个数或类型不同
class Complex{
private:
double real,imgae;
public:
void Set(double r, double i);
Complex(double r,double i);
Complex(double r);
Complex(Complex c1,Complexc2);
}; Complex::Complex(double r, double i)
{
real = r;imag = i;
} Complex::Complex(double r)
{
real = r; imag = 0;
}
Complex::Complex(Complex c1, Complex c2)
{
real = c1.real+c2.real;
imag = c1.imag+c2.imag;
}
Complex c1(3),c2(1,0),c3(c1,c2);
//c1={3,0} c2={1,0} c3={4,0};

构造函数在数组中的使用

class CSample{
int x;
public:
CSample(){
cout<<"Constructor 1 Called"<<endl;
}
CSample(int n){
x = n;
cout<<"Constructor 2 Called"<<endl;
}
}; int main()
{
CSample array1[2]; // 1 1
cout<<"step1"<<endl;
CSample array2[2] = {4,5};//2 2
cout<<"step2"<<endl;
CSample array3[2] = {3};//2 1
cout<<"step3"<<endl;
CSample *array4 = new CSample[2];//1 1
delete []array4; // 收回空间
return 0;
}
class Test{
public:
Test(int n){ }//(1)
Test(int n, int m){ }//(2)
Test(){ }//(3)
};
Test array1[3] = {1,Test(1,2)} //三个元素分别用(1),(2),(3)进行初始化
Test array2[3] = {Test(2,3),Test(1,2),1};//2 2 1
Test * pArray[3] = {new Test(4), new Test(1,2)};//1 2 ❌

复制构造函数(copy constructor)

  • 基本概念

    • 只有一个参数,即对同类对象的引用
    • 形如 X::X( X&)或X::X(const X &),二选一,后者能以常量对象作为参数
    • 如果没有定义复制构造函数,那么编译器生成默认复制构造函数,默认的复制构造函数完成复制功能
    • 如果定义了复制构造函数,默认的复制构造函数将不存在。
class Complex{
private:
double real,imag;
};
Complex c1;//调用缺省无参构造函数
Complex c2(c1);//调用缺省的复制构造函数,将c2初始化成和c1一样
class Complex{
public:
double real,imag;
Complex(){ }
Complex(const Complex & c){
real = c.real;
imag = c.imag;
cout<<"Copy Constructor called";
}
};
Complex c1;
Complex c2(c1);
  • 注意

    • 不允许有形如X::X(X)的构造函数
class CSample{
CSample(CSample c){
//error,不允许这样的构造函数
}
}
  • 复制构造函数起作用的三种情况

    1. 当用一个对象去初始化同类的另一个对象时

      Complex c2(c1);
      
      Complex c2 = c1; //初始化语句,非赋值语句
    2. 如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用

      class A
      {
      public:
      A(){ };
      A(A & a){
      cout<<"Copy constructor called"<<endl;
      }
      } void Func(A a1){ }
      int main(){
      A a2;
      Func(a2);
      return 0;
      }
    3. 如果函数的返回值时类A的对象时,则函数返回时,A的复制构造函数将被调用

  • 为什么要自己写复制构造函数

    • 后续会讲解

类型转换构造函数

  • 目的

    • 实现类型的自动转换
  • 特点
    • 只有一个参数
    • 不是复制构造函数
  • 编译系统会自动调用➡️转换构造函数➡️建立一个临时对象/临时变量
class Complex{
public:
double real,imag;
Complex(int i){//类型转换构造函数
cout<<"IntConstructor called"<<endl;
real = i;
imag = 0;
}
Complex(double r, double i) //传统构造函数
{
real = r;
imag = i;
}
}; iint main()
{
Complex c1(7,8);//对c1进行初始化,调用传统构造函数
Complex c2 = 12; //对c2进行初始化,调用类型转换构造函数,不会生成一个临时对象
c1 = 9; // 赋值语句,虽然赋值号两边类型不同,但是编译器没有报错。
//编译器以9作为实参,调用类型转换构造函数,9被自动转换成一个临时Complex对象,赋值给c1
}

析构函数(Destructor)

  • 回顾:构造函数

    • 成员函数的一种
    • 名字与类名相同
    • 可以有参数,不能有返回值
    • 可以有多个构造函数
    • 用来初始化对象
  • 析构函数
    • 成员函数的一种
    • 名字与类名相同(在函数名之前加'~')
    • 无参数,无返回值
    • 一个类最多只能有一个析构函数
    • 在对象消亡时,自动被调用
      • 在对象消亡前做善后工作

        • 释放分配的空间等
    • 定义类时没写析构函数,则编译器生成缺省析构函数
      • 不涉及释放用户申请的内存释放等清理工作
    • 定义了析构函数,则编译器不生成缺省析构函数
class String{
private:
char *p;
public:
String(){
p = new char[10];
}
~String();
}; String::~String(){
delete [] p;
}
  • 析构函数和数组

    • 对象数组生命周期结束时,对象数组的每个元素的析构函数都会被调用
  • 析构函数和运算符delete
    • delete运算导致析构函数调用
  • 例题总结
    • 先被构造的对象,最后被析构掉(平级的情况下)
    • 构造函数和析构函数在不同编译器中
      • 个别调用情况不一致

        • 编译器有bug
        • 代码优化措施
      • 课程中讨论的是C++标准的规定,不牵扯编译器的问题

静态成员变量和静态成员函数

基本概念

  • 静态成员:在说明前面+static关键字的成员,有静态成员变量和静态成员函数
  • 普通成员变量每个对象有各自的一份,而静态成员变量一共一份,所有对象共享
  • sizeof运算符不会计算静态成员变量
  • 普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象
  • 静态成员不需要通过对象就能访问
  • 静态成员变量本质上是全局变量
  • 静态成员函数本质上是全局函数
  • 设置静态成员这种机制,目的是将和某些类紧密相关的全局变量和函数写进类中,看上去像一个整体,易于维护和理解

如何访问静态成员

  1. 类名::成员名
  2. 对象名.成员名
  3. 指针->成员名
  4. 引用.成员名

静态成员示例

  • 考虑一个需要随时知道矩形总数和总面积的图形处理程序
  • 可以用全局变量来记录总数和总面积(造成变量和类之间的关系不直观,且变量能够被其他类访问,存在一定风险)
  • 用静态成员将这两个变量封装进类中,更容易理解和维护
  • 必须在定义类的文件中对静态成员变量进行一次说明or初始化,否则编译能通过,链接不能通过

注意事项

  • 在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数
  • 考虑到复制构造函数的影响

成员对象和封闭类

成员对象

  • 一个类的成员变量是另一个类的对象
  • 包含成员对象的类叫封闭类(Enclosing)
class CTyre{ //轮胎类
private:
int radius;
int width;
public:
CTyre(int r,int w):radius(r),width(w){ } //这种风格看起来更好一些
};
class CEngine{ //引擎类
} class CCar{ //汽车类➡️“封闭类”
private:
int price;//价格
CTyre tyre;
CEngine engine;
public:
CCar{int p, int tr, int tw};
};
CCar::CCar(int p, int tr,int w):price(p),tyre(tr,w){ }; int main()
{
CCar car(20000,17,225);
return 0;
}
  • 如果CCar类不定义构造函数,则:

    • CCar car;//error➡️编译错误
    • 编译器不知道car.type该如何初始化

    • car.engine的初始化没有问题,可以用默认构造函数

  • 生成封闭类对象的语句➡️明确“对象中的成员对象”➡️如何初始化

封闭类构造函数的初始化列表

  • 定义封闭类的构造函数时,添加初始化列表

    • 类名::构造函数(参数表):成员变量1(参数表),成员变量2(参数表),...

      {

      ......

      }

    • 成员对象初始化列表中的参数

      • 任意复杂的表达式
      • 函数/变量/表达式中的函数,变量有定义

调用顺序

  • 当封闭类对象生成

    • 执行所有成员对象的构造函数
    • 执行封闭类的构造函数
  • 成员对象的构造函数调用顺序
    • 和成员对象在类中的说明顺序一致
    • 与在成员初始化列表中出现的顺序无关
  • 当封闭类对象消亡
    • 执行封闭类的析构函数
    • 执行成员对象的析构函数
  • 先构造的后析构,后构造的先析构

友元

友元函数

  • 一个类的友元函数可以访问该类的私有成员
class CCar; // 提前声明CCar类,以便后面CDriver类使用
class CDriver{
public:
void ModifyCar(CCar* pCar); //改装汽车
};
class CCar{
private:
int price;
friend int MostExpensiveCar(CCar cars[],int total); //声明友元
friend void CDriver::ModifyCar(CCar *pCar);
} void CDriver::ModifyCar(CCar *pCar)
{
pCar->price +=1000; //汽车改装后价值增加
}
int MostExpensiveCar(CCar cars[],int total)//求最贵的汽车的价格
{
int tmpMax = -1;
for(int i = 0; i < total;++i)
if(cars[i].price > tmpMax)
tmpMax = cars[i].price;
return tmpMax;
}
int main()
{
return 0;
}
  • 可以将一个类的成员函数(包括构造,析构函数)定义成另一个类的友元
class B{
public:
void function();
}; class A{
friend void B::function();
};

友元类

  • A是B的友元类➡️A的成员函数可以访问B的私有成员

    • 友元类之间的关系,不能传递,不能继承
class CCar{
private:
int price;
friend class CDriver; //声明CDriver为友元类
};
class CDriver{
public:
CCar myCar;
void ModifyCar(){
myCar.price += 1000; //CDriver是CCar的友元类➡️可以访问其私有成员
}
};
int main(){return 0;}

this指针

this指针作用

  • 指向成员函数所作用的对象
  • 非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针
class Complex{
public:
double real,imag;
void Print(){
cout<<real<<","<<imag;
}
Complex(double r, double i):real(r),imag(i){ }
Complex AddOne(){
this->real++; //=real++
this->Print();//=Print()
return *this;
}
}; int main()
{
Complex c1(1,1),c2(0,0);
c2 = c1.AddOne();
return 0;
}
class A{
int i;
public:
void Hello(){cout<<"hello"<<endl;}
};//编译器把该成员函数编译成机器指令后,会变成
//void Hello(A *this){cout<<"hello"<<endl;}
//如果Hello函数变成 void Hello(){cout<<i<<"hello"<<endl;}
//就会出错 int main()
{
A *p = NULL;
p->Hello(); //结果会怎样?
}//输出:hello
//编译器把该成员函数编译成机器指令后,会变成
//hello(p)

注意事项

  • 静态成员函数中不能使用this指针
  • 因为静态成员函数并不具体作用于某个对象
  • 因此,静态成员函数的真实的参数的个数,就是程序中写出的参数的个数

常量对象、常量成员函数和常引用

常量对象

  • 如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加const关键字

常量成员函数

  • 在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数
  • 常量成员函数执行期间不应修改其所作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)
class Sample
{
public:
int value;
void GetValue() const;
void func(){};
Sample(){}
};
void Sample::GetValue() const
{
value = 0;//wrong
func();//wrong
} int main(){
const Sample o;
o.value = 100; //err,常量对象不可以被修改
o.func();//err常量对象上面不能执行非常量成员函数
o.GetValue();//ok
return 0;
}

常量成员函数的重载

  • 两个成员函数,名字和参数表都一样,但是一个是const一个不是,算重载

常引用

  • 引用前面可以加const关键字,成为常引用。不能通过常引用,修改其引用的变量
const int & r = n;
r = 5;//error
n = 4;//ok
  • 对象作为函数的参数时,生成该参数需要调用复制构造函数,效率比较低。用指针做参数,会让代码的可读性变差
  • 所以可以用对象的引用作为参数。
  • 但对象引用作为参数有一定的风险,若函数中不小心修改了形参,则实参也会跟着改变,如何避免?
  • 所以可以用对象的常引用作为参数
  • 这样函数中就能确保不会出现无意中更改形参值的语句了

练习题

注:填空题在Coursera提交时,文件中只需出现填进去的内容即可

Quiz 1

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<string>
#include<string.h>
using namespace std;
class A {
public:
int val;
A (int n = 0){val = n;}
A & GetObj(){
return *this;
}
};
int main() {
A a;
cout << a.val << endl;
a.GetObj() = 5;
cout << a.val << endl;
}

Quiz 2

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<string>
#include<string.h>
using namespace std;
class Sample{
public:
int v;
Sample(int n):v(n) { }
Sample(const Sample &a)
{
v = a.v*2;
}
};
int main() {
Sample a(5);
Sample b = a;
cout << b.v;
return 0;
}

Quiz 3

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<string>
#include<string.h>
using namespace std;
class Base {
public:
int k;
Base(int n):k(n) { }
};
class Big {
public:
int v;
Base b;
Big(int n = 5):v(n),b(n){ };
};
int main() {
Big a1(5); Big a2 = a1;
cout << a1.v << "," << a1.b.k << endl;
cout << a2.v << "," << a2.b.k << endl;
return 0;
}

Quiz 4 魔兽世界之一:备战

#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
const int WARRIOR_NUM = 5;
/*
string Warrior::names[WARRIOR_NUM] = { "dragon","ninja","iceman","lion","wolf" };
红方司令部按照 iceman、lion、wolf、ninja、dragon 的顺序制造武士。
蓝方司令部按照 lion、dragon、ninja、iceman、wolf 的顺序制造武士。
*/ class Headquarter;
class Warrior
{
private:
Headquarter * pHeadquarter; //指向英雄所属阵营的指针
int kindNo;//武士的种类编号 0 dragon 1 ninja 2 iceman 3 lion 4 wolf
int no;//英雄编号
public:
static string names[WARRIOR_NUM]; //存放5种职业名字的数组
static int initialLifeValue [WARRIOR_NUM]; //存放不同英雄的起始生命值(从输入中采集)
Warrior( Headquarter *p,int no_,int kindNo_);//构造函数
void PrintResult(int nTime); //执行打印数据的工作,若无法继续创建则输出结束并停止
}; class Headquarter
{
private:
int totalLifeValue;
bool stopped;
int totalWarriorNum;
int color;
int curMakingSeqIdx; //当前要制造的武士是制造序列中的第几个
int warriorNum[WARRIOR_NUM]; //存放每种武士的数量
Warrior * pWarriors[1000];//和每个创建的英雄建立链接
public:
friend class Warrior;
static int makingSeq[2][WARRIOR_NUM];//武士的制作序列,按阵营的不同分成两个
void Init(int color_, int lv); //初始化阵营需要颜色和总血量
~Headquarter();
int Produce(int nTime); //创建英雄,输入时间
string GetColor();
}; Warrior::Warrior(Headquarter *p, int no_, int kindNo_) {
no = no_;
kindNo = kindNo_;
pHeadquarter = p;
} void Warrior::PrintResult(int nTime) {
string color = pHeadquarter->GetColor();
printf("%03d %s %s %d born with strength %d,%d %s in %s headquarter\n",
nTime, color.c_str(), names[kindNo].c_str(),no,initialLifeValue[kindNo],
pHeadquarter->warriorNum[kindNo],names[kindNo].c_str(),color.c_str()); // string 在printf中输出的函数调用c_str()
} void Headquarter::Init(int color_, int lv) {
color = color_;
totalLifeValue = lv;
totalWarriorNum = 0;
stopped = false;
curMakingSeqIdx = 0;
for (int i = 0; i < WARRIOR_NUM; i++) {
warriorNum[i] = 0;
}
} Headquarter::~Headquarter() {
for (int i = 0; i < totalWarriorNum; i++) {
delete pWarriors[i];
}
} int Headquarter::Produce(int nTime) {
if(stopped)
return 0;
int searchingTimes = 0;
while(Warrior::initialLifeValue[makingSeq[color][curMakingSeqIdx]] > totalLifeValue &&
searchingTimes < WARRIOR_NUM)
{
curMakingSeqIdx = (curMakingSeqIdx + 1) % WARRIOR_NUM;
searchingTimes++;
}
int kindNo = makingSeq[color][curMakingSeqIdx];
if(Warrior::initialLifeValue[kindNo] > totalLifeValue)
{
stopped = true;
if(color == 0)
printf("%03d red headquarter stops making warriors\n",nTime);
else
printf("%03d blue headquarter stops making warriors\n",nTime);
return 0;
}
//排除所有其他条件后,开始制作士兵
totalLifeValue -= Warrior::initialLifeValue[kindNo];
curMakingSeqIdx =( curMakingSeqIdx + 1) % WARRIOR_NUM;
pWarriors[totalWarriorNum] = new Warrior(this,totalWarriorNum+1,kindNo);
warriorNum[kindNo]++;
pWarriors[totalWarriorNum]->PrintResult(nTime);
totalWarriorNum++;
return 1;
} string Headquarter::GetColor() {
if(color == 0)
return "red";
else
return "blue";
} string Warrior::names[WARRIOR_NUM] = {"dragon","ninja","iceman","lion","wolf"};
int Warrior::initialLifeValue[WARRIOR_NUM];
int Headquarter::makingSeq[2][WARRIOR_NUM]={{2,3,4,1,0},{3,0,1,2,4}};//两个司令部武士的制作顺序序列 int main()
{
int t;
int m;
Headquarter RedHead,BlueHead;
scanf("%d", &t); //读取case数
int nCaseNo = 1;
while(t--){
printf("Case:%d\n",nCaseNo++);
scanf("%d",&m);//读取基地总血量
for (int i = 0; i < WARRIOR_NUM; i++) {
scanf("%d",&Warrior::initialLifeValue[i]);
}
RedHead.Init(0,m);
BlueHead.Init(1,m);
int nTime = 0;
while (true){
int tmp1 = RedHead.Produce(nTime);
int tmp2 = BlueHead.Produce(nTime);
if( tmp1 == 0 && tmp2 == 0)
break;
nTime++;
}
}
return 0;
}
//老师给的答案读了好几遍,大概捋顺了……
//现阶段自己根本写不出来这种程序,在第一步抽象出两个类这块就感觉很困难
//慢慢加油吧……

Coursera课程笔记----C++程序设计----Week3的更多相关文章

  1. Coursera课程笔记----C程序设计进阶----Week 5

    指针(二) (Week 5) 字符串与指针 指向数组的指针 int a[10]; int *p; p = a; 指向字符串的指针 指向字符串的指针变量 char a[10]; char *p; p = ...

  2. Coursera课程笔记----C程序设计进阶----Week 4

    指针(一) (Week 4) 什么是"指针" 互联网上的资源--地址 当获得一个地址,就能得到该地址对应的资源,所以可以把"网址"称为指向资源的"指针 ...

  3. Coursera课程笔记----C程序设计进阶----Week 3

    函数的递归(Week 3) 什么是递归 引入 函数可以嵌套调用:无论嵌套多少层,原理都一样 函数不能嵌套定义:不能在一个函数里再定义另一个函数,因为所有函数一律平等 问题:一个函数能调用它自己吗? 举 ...

  4. Coursera课程笔记----C程序设计进阶----Week 1&2

    C程序中的函数(Week 1&2) 函数 函数的定义 对函数的普遍认识:y=f(x) C语言中的常用函数: 平方根: r = sqrt(100.0) 底数x的y次幂:k = pow(x,y) ...

  5. 操作系统学习笔记----进程/线程模型----Coursera课程笔记

    操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...

  6. Coursera课程笔记----Write Professional Emails in English----Week 3

    Introduction and Announcement Emails (Week 3) Overview of Introduction & Announcement Emails Bas ...

  7. Coursera课程笔记----Write Professional Emails in English----Week 1

    Get to Know Basic Email Writing Structures(Week 1) Introduction to Course Email and Editing Basics S ...

  8. Coursera课程笔记----计算导论与C语言基础----Week 6

    理性认识C程序 导论(Week 6) 明确学习进度 讲课内容 感性➡️理性➡️函数➡️指针等 作业练习 初级阶段 ➡️正常作业练习 C语言的由来 程序设计语言的分类 低级语言之机器语言 0010101 ...

  9. Coursera课程笔记----计算导论与C语言基础----Week 4

    感性认识计算机程序(Week 4) 引入 编程序 = 给计算机设计好运行步骤 程序 = 人们用来告诉计算机应该做什么的东西 问题➡️该告诉计算机什么?用什么形式告诉? 如果要创造一门"程序设 ...

随机推荐

  1. 基于tcp协议的套接字通信:远程执行命令

    要解决粘包问题: TCP:流式协议 特点: 1.数据流没有开头也没有结果,像水流一样 2.TCP协议有一个nagle算法, nagle算法会将数据量较小,并且时间间隔较短的数据合成一条数据发送, 这么 ...

  2. Caused by:java.lang.ClassNotFoundException:org.apache.hadoop.yarn.util.Apps

    错误原因 缺少hadoop-yarn.jar包. 导入jar包就好了~-~

  3. deepin下深度终端使用ssh-agent(xshell中的xagent功能)

    背景:从windows10换到deepin后,在连接公司的服务器遇到了问题:windows下用的是xshell,开启xagent后,可直接从公司的跳转板上连接生产服务器:在deepin的深度终端上,从 ...

  4. vector做形参时的三种传参方式

    vector在做形参的时候传参的方式和普通的变量是一样的,要么传值.要么传引用.要么传指针. 现在分别定义三个以vector为形参的函数: (1) fun1(vector <int> v) ...

  5. JavaScript--'data-'的用法(1)

    HTML5为我们提供了一个强大的功能,前段也也能实现后台数据库的效果,例如data-xxx <a href="#myModal" data-industry_id=" ...

  6. 查看现有的 cipher suite

    openssl ciphers [-v] [-ssl2] [-ssl3] [-tls1] [cipherlist]

  7. latex-列表环境

    介绍 latex 主要有三种列表环境,进行罗列的实现, 无序列表 -- itemize 有序列表 -- enumerate 描述列表 -- description 本文进行了一一介绍和演示, 同时添加 ...

  8. C++-doctest-测试框架

    C++-doctest-测试框架 C++UnitTestDoctest 测试框架 doctest 是用过的最简单好用的的单元测试框架, 只需要引用 一个头文件即可 无main 函数的测试样例 #def ...

  9. Visual Studio Code mac OS 安装 中文简体语言包

    先下载中文简体语言包 官网 https://marketplace.visualstudio.com/search?target=VSCode&category=Language%20Pack ...

  10. [mysql]linux mysql 读写分离

    [mysql]linux mysql 读写分离 作者:flymaster qq:908601287 blog:http://www.cnblogs.com/flymaster500/ 1.简介 当今M ...