C++学习6-面向对象编程基础(运算符重载、类的派生与继承、命名空间)
运算符重载
重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符号共同组成。重载的运算符是遵循函数重载的选择原则,根据不同类型或不同参数来选择不同的重载运算符。
运算符重载的基本语法
- 成员函数形式
<类名> operator<符号>(<参数表>)
参数表列出该运算符需要的操作数。
运算符函数体对重载的运算符的含义做出新的解释。这种解释仅局限在重载该运算符的类中,即当在X类对象的关联中,运算符含义由函数体解释;否则脱离类对象,该运算符具有系统预定义的含义;
- 友元函数形式
friend <类名> operator<符号>(<参数表>)
选择成员或者非成员函数
当我们定义重载的运算符时,必须首先决定是将其声明为类的成员函数还是声明为一个普通的非成员函数。下面的准则有助于我们在将运算符定义为成员函数还是普通的非成员函数做出抉择;
- 赋值(=)、下标([])、调用(()) 和成员访问箭头(->)运算符必须是成员。
对于特殊符号如 =, ->, [], () 在使用运算符重载时,因为当编译器发现当类中没有定义这4个运算符的重载成员函数时,就会自己加入默认的运算符重载成员函数。影响了重载的结果,且还需要注意浅拷贝与深拷贝的问题。
- 复合赋值运算符一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。
- 改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符,通常应该是成员;
- 只能用友元、不用成员函数的情况
- 具有对称性的运算符可能转换任意一端的运算对象,例如输入输出流>>、<<等符号,只能用友元函数重载。
例如:如果是重载双目操作符(即为类的成员函数),如果运算符重载为成员函数,第一个参数必须是本类的对象。
我们使用>> 或<< 时,而cin与cout是系统默认的关键字而非对象名,而左侧运算量又必须是对象名。所以作为成员函数引用时是以下这个样子的:
Iamclassobj << cout;
可这样的用法并不符合常规的应用习惯。
使用友元函数时可以不强制要求左侧运算量必须是对象本身,为了更好的符合代码书写系统,不改变默认符号的原本意义,所以通常使用友元函数对输入、输出流符号进行运算符重载;
详细代码见以下
【特殊运算符重载(输入输出运算符 )】
使用区别:
重载为成员函数时,会隐含一个this指针;
当重载为友元函数时,将不存在隐含的this指针,需要在参数列表中显式地添加操作数;
不能运算符重载的符号
> 作用域运算符 ::
> 条件运算符 ?:
> 直接成员访问运算符 .
> 取大小运算符 sizeof
> 指针分量运算符 ->
成员/友元函数形式的运算符重载引用形式
我们除了将运算符作用于类型正常的实参,隐式调用重载运算符函数外。还可以显示调用,像调用普通函数一样调用运算符函数,先指定函数名字,然后传入实参;
对比格式如下:
Tables | 成员函数形式 | 友元函数形式 |
---|---|---|
一元运算 | 隐式:对象# | 隐式:对象# |
显式:对象.operator#() | 显式:operator#(对象) | |
二元运算 | 隐式:对象A#对象B | 隐式:对象A#对象B |
显式:对象A.operator#(对象B) | 显式:operator#(对象A,对象B) |
运算符重载的基本语法(成员函数 - 单目运算符)
下面的代码以 前置++ 与 后置++ 的运算符重载语法为例编写;
- 前置++运算符:前增量操作数与返回值是同一个变量。要求返回的参数是该对象参数的引用,也就是指向操作数的this指针;
- 后置++运算符:后置操作符不要求返回值为引用,为传递过来的形参定义了一个类类型的临时变量,将this指针赋值给临时变量,对this指针指向的数值增加,然后返回临时变量的值,这里不需要引用;
实例代码:成员函数-单目运算符
class CTest
{
public:
CTest(int val) { m_nNumber = val; };
//前置++
CTest & operator++()
{
m_nNumber++;
return *this;
}
//后置++
CTest operator++(int)
{
CTest temp = *this;
m_nNumber++;
return temp;
}
~CTest() {};
private:
int m_nNumber;
};
int main()
{
CTest objNumber(100);
CTest re = objNumber++;
CTest re2 = ++objNumber;
CTest re3 = objNumber;
return 0;
}
成员函数-双目运算符
class CNumber {
public:
CNumber(int val) { m_nValue = val; }
CNumber operator+(CNumber & op2) {
CNumber temp = *this;
temp.m_nValue = m_nValue + op2.m_nValue;
return temp;
}
private:
int m_nValue;
};
int main()
{
CNumber cNumA(100), cNumB(200);
CNumber cNumC = cNumA + cNumB;
return 0;
}
友元函数-单目运算符
使用友元函数进行运算符重载,需要在类内使用friend关键字声明运算符重载函数为友元函数。
然后定义运算符重载函数时,在参数列表内定义类类型的形参。
class CNumber {
public:
CNumber(int val) { m_nValue = val; }
friend CNumber & operator++(CNumber & op1); //前置++
friend CNumber operator++(CNumber &op1, int);//后置++
private: int m_nValue;
};
CNumber & operator++(CNumber & op1) {
op1.m_nValue = op1.m_nValue + 1;
return op1;
}
CNumber operator++(CNumber & op1, int) {
CNumber temp = op1;
op1.m_nValue = op1.m_nValue + 1;
return temp;
}
int main() {
CNumber cNumA(100);
CNumber cNumB = cNumA++;
CNumber cNumD = ++cNumA;
//CNumber cNumC = cNumA.operator++();
return 0;
}
友元函数-双目运算符
class CNumber {
public:
CNumber(int val) { m_nValue = val; }
friend CNumber operator+(CNumber & op1, CNumber & op2);
private:
int m_nValue;
};
CNumber operator+(CNumber & op1, CNumber & op2)
{
CNumber temp(0);
temp.m_nValue = op1.m_nValue + op2.m_nValue;
return temp;
}
int main()
{
CNumber cNumA(100), cNumB(200);
CNumber cNumC = cNumA + cNumB;
return 0;
}
特殊运算符重载(赋值运算符 [ ] )
文中使用了strdup函数,strdup函数返回指向被复制的字符串的指针,所需空间由malloc()函数分配且可以由 free() 函数释放。stdrup可以直接把要复制的内容复制给没有初始化的指针,因为它会自动分配空间给目的指针;
#include "stdafx.h"
#include <string.h>
#include <malloc.h>
#include <iostream>
using namespace std;
class MyString {
public:
MyString(const char * buf) {
m_szBuf = _strdup(buf);
}
~MyString() {
free(m_szBuf); //不要忘记释放动态分配出来的内存
}
char operator[](int pos)
{
return m_szBuf[pos];
}
private:
char * m_szBuf;
};
int main()
{
MyString str("hello world");
cout << str[8];
return 0;
}
特殊运算符重载(赋值运算符 = )
以下代码中第33行,str2是调用赋值运算符的对象,str1是参数,34行转换构造+赋值运算符,str2是调用函数,而字符串被转换构造后进入赋值运算符中。
示例代码
#include "stdafx.h"
#include <string.h>
#include <malloc.h>
class MyString {
public:
MyString(const char*buf) {
m_szBuf = _strdup(buf);
}
~MyString() {
free(m_szBuf);
}
MyString & operator=(const MyString & str)
{
free(m_szBuf);
m_szBuf = _strdup(str.m_szBuf);
return *this;
}
private:
char * m_szBuf; //注意浅拷贝问题!!
};
int main() {
MyString str1("今晚打老虎"), str2("test");
str2 = str1; //str2是调用赋值运算符的对象,str1是参数
str2 = "dddd"; //转换构造+赋值运算符,str2是调用函数,而字符串被转换构造后进入赋值运算符中
MyString str3 = "ffff"; //转换构造
//MyString str4 = str1; //拷贝构造(默认转换构造是浅拷贝,所以释放时会出错)
return 0;
}
特殊运算符重载(类型转换运算符 )
operator关键字除了可以重载运算符外还可以重载变量类型,但语法比较特殊;
- 1 不用声明返回值
- 2 转换的类型就是返回之后,同时也是函数名
#include "stdafx.h"
#include <iostream>
using namespace std;
class MyString {
public:
MyString(const char * buf)
{ m_szBuf = _strdup(buf); }
~MyString() { free(m_szBuf); }
//语法比较特殊
//1. 不用声明返回值
//2. 转换的类型就是返回之后,同时也是函数名
operator int() { return strlen(m_szBuf); }
operator char *() { return m_szBuf; }
operator char() { return m_szBuf[0]; }
private:
char * m_szBuf;
};
int main() {
MyString str("hello world");
int strLen = (int)str;
char * pStr = (char *)str;
char firstChar = (char)str;
cout << pStr << ":" << strLen << endl;
return 0;
}
特殊运算符重载(输入输出运算符 )
如果是重载双目操作符(即为类的成员函数),如果运算符重载为成员函数,第一个参数必须是本类的对象。
我们使用>> 或<< 时,而cin与cout是系统默认的关键字而非对象名,而左侧运算量又必须是对象名。所以作为成员函数引用时是以下这个样子的:
Iamclassobj << cout;
可这样的用法并不符合常规的应用习惯。
使用友元函数时可以不强制要求左侧运算量必须是对象本身,为了更好的符合代码书写系统,不改变默认符号的原本意义,所以通常使用友元函数对输入、输出流符号进行运算符重载;
详细代码见以下
【特殊运算符重载(输入输出运算符 )】
#include "stdafx.h"
#include <iostream>
using namespace std;
class MyString {
public:
MyString(const char * buf) { m_szBuf = _strdup(buf); }
~MyString() { free(m_szBuf); }
//输出,返回ostream方便连续输出
friend ostream& operator<<(ostream & output, MyString &str) {
return output << str.m_szBuf;
}
//输入,返回istream方便连续输出
friend istream& operator>>(istream & input, MyString & str) {
return input >> str.m_szBuf; //注意str.m_szBuf的长度
}
//成员函数重载
// ostream & operator >>(ostream & output)
// {
// output << m_szBuf;
// return output;
// }
private:
char * m_szBuf;
};
int main() {
MyString str1("hello world"), str2("基础最重要");
cout << str1 << ":" << str2 << endl;
cin >> str1 >> str2;
return 0;
}
特殊运算符重载(字符串连接运算符 +)
#include "stdafx.h"
#include <string.h>
#include <malloc.h>
class MyString {
public:
MyString(const char * buf) { m_szBuf = _strdup(buf); }
~MyString() { free(m_szBuf); }
MyString(MyString &obj) { m_szBuf = _strdup(obj.m_szBuf); }
MyString& operator=(MyString & obj) {
free(m_szBuf); m_szBuf = _strdup(obj.m_szBuf);
return *this;
}
//成员函数重载
// MyString operator+(const MyString & str2) {
// int len = strlen(m_szBuf) + strlen(str2.m_szBuf) + 1;
// char * buf = new char[len];
// memset(buf, 0, len);
// strcat_s(buf, len, m_szBuf);
// strcat_s(buf, len, str2.m_szBuf);
//
// MyString temp(buf);
// delete[] buf;
// return temp;
// }
//友元函数重载
friend MyString operator+(const MyString & str1,const MyString & str2) {
int len = strlen(str1.m_szBuf) + strlen(str2.m_szBuf) + 1;
char * buf = new char[len];
memset(buf, 0, len);
strcat_s(buf, len, str1.m_szBuf);
strcat_s(buf, len, str2.m_szBuf);
MyString temp(buf);
delete[] buf;
return temp;
}
private:
char * m_szBuf;
};
int main()
{
MyString str1("hello"), str2(" world"), str3(" ");
str3 = str1 + str2;
str3 = str1 + " world";
str3 = "hello" + str2;
return 0;
}
继承与派生
在C++中,如果有一个类B继承了类A,或从类A派生出类B,通常称类A为基类(父类),称类B为派生类(子类),类B不但拥有类A的属性,而且还可以拥有自己新的属性;
- 单继承:派生类只有一个直接基类的继承方式;
- 多继承:派生类有多个直接基类的继承方式;
- 虚函数: 对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数;
访问与控制
继承方式:派生类是按指定的继承方式派生的,继承方式有:
- public : 公有继承
基类的每个成员再派生类种保持同样的访问权限;
- private:私有继承
基类中的每个成员在派生类中都是private成员,而且它们不能再被派生的子类所访问;
- protected :保护继承
基类中的public成员和protected成员再派生类中都是protected成员,private成员在派生类中仍为private成员
实例代码
#include "stdafx.h"
#include <iostream>
using namespace std;
class CClassA {
public:
CClassA(int nNum) { m_nNumA = nNum; }
protected:
int m_nNumA;
void fun() {}
private:
int m_nNumB;
};
class CClassB :private CClassA {
public:
CClassB(int nNum) :CClassA(nNum) {
// ......
}
void print() {
fun();
cout << m_nNumA << m_nNumB << endl;
}
CClassA::fun;
CClassA::m_nNumA;
//CClassA::m_nNumB; //父类的私有属性,子类不能访问
};
int _tmain(int argc, _TCHAR* argv[]) {
CClassB objB(15);
objB.print();
objB.fun();
objB.m_nNumA;
//objB.m_nNumB;
return 0;
}
派生类的定义格式
单继承方式
单继承的方式的格式如下:
class <派生类名> :<继承方式> <基类名>
{
}
实例代码
#include "stdafx.h"
class Base
{
public:
Base() :m_a(0) , m_b(0)
{
}
void print()
{
printf("我是父类");
}
private:
int m_a;
int m_b;
};
class CTest :public Base
{
public:
private:
};
int _tmain(int argc, _TCHAR* argv[])
{
CTest obj;
obj.print();
return 0;
}
多继承方式
多继承派生类有多个基类,基类名之间用逗号分离,每个基类名前都有一个该基类的继承方式说明;
缺省的继承方式为私有继承;
class <派生类名>:<继承方式1> <基类名1>,<继承方式2> <基类名2>
{
}
实例代码
#include "stdafx.h"
class Base1
{
public:
void FunBase1()
{
printf("我是基类1");
}
};
class Base2
{
public:
void FunBase2()
{
printf("我是基类2");
}
};
class CTest :public Base1 , public Base2
{
public:
void FunTest()
{
printf("我是子类");
}
void fun()
{
FunBase1();
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CTest obj;
obj.FunBase1();
obj.FunBase2();
obj.FunTest();
obj.fun();
return 0;
}
构造析构调用顺序
自己敲了一遍代码,发现派生之后,运行构造函数前是先调用了基类的构造函数,再调用子类的构造函数。而调用析构的时候顺序与调用构造函数的顺序相反,先调用子类的析构函数,再调用基类的构造函数;
#include "stdafx.h"
#include <iostream>
using namespace std;
class CClassA {
public:
CClassA() { cout << "CClassA()" << endl; }
CClassA(int nNum) { cout << "CClassA(int)" << endl; }
~CClassA() {
cout << "类A析构" << endl;
};
};
class CClassB :public CClassA {
public:
CClassB() { cout << "CClassB()" << endl;; }
//为何这里调用的时候不调用类A的同名函数,而是调用了基类的构造函数??
CClassB(int nNum) { cout << "CClassB(int)" << endl; }
//因为默认情况下首先调用类A的无参默认构造函数,如果想要调用类A有参构造函数需要自定义传参进类A的有参构造函数
//CClassB(int nNum) :CClassA(nNum){ cout << "CClassB(int)" << endl; }
CClassB(int nNumA, int nNumB, int nNumC)
:CClassA(nNumA), m_nNumB(nNumB), m_nNumC(nNumC) {
cout << "CClassB(int,int,int)" << endl;
}
~CClassB() { cout << "类B析构" << endl; };
private: int m_nNumB, m_nNumC;
};
int _tmain(int argc, _TCHAR* argv[]) {
//调用父类对象,并不会调用子类的函数
CClassA objAA;
CClassA objAB(16);
//调用子类对象,会调用父类的默认构造函数,而传入参数后才可调用父类同名同参函数
CClassB objA;
CClassB objB(15);
CClassB objC(1, 5, 6);
return 0;
}
派生类的二义性问题(重定义)
当子类与父类中有相同的函数名时,会优先调用子类的成员函数,如果想要调用父类的成员函数,需要指明调用哪个类的成员函数,格式如下:
<对象名>.<父类名>::<成员函数>;
objB.CClassA::printf_A();
例如:
#include "stdafx.h"
#include <iostream>
using namespace std;
class CClassA
{
public:
void printf_A() { cout << "ClsA printf_A!\n" << endl; }
void printf_B(int nNum) { cout << "ClsA printf_B!\n" << endl; }
};
class CClassB :public CClassA {
public:
void printf_A(int nNum) { cout << "ClsB printf_A!\n" << endl; }
void printf_B() { cout << "ClsB printf_B!\n" << endl; }
};
int _tmain(int argc, _TCHAR* argv[])
{
CClassB objB;
objB.printf_A(15);
objB.printf_A(); //子类通过重定义,覆盖掉了父类的方法
objB.CClassA::printf_A(); //必须通过类名来显式访问被重定义的方法
objB.printf_B();
objB.printf_B(15);
return 0;
}
虚基类
引用虚基类的目的是为了解决二义性问题,使用公共基类在其派生类对象中只产生一个基类子对象,其目的是使公共基类在其派生对象中只产生一个基类子对象;
使用格式如下:
virtual <继承方式> <基类名>
virtual public CClassA
实例代码
#include "stdafx.h"
#include <iostream>
using namespace std;
class CClassA {
public: void fun_a() { cout << "fun_a:CClassA\n"; }
};
class CClassB1 : virtual public CClassA {
public: void fun_b() { cout << "fun_b:CClassB1\n"; }
};
class CClassB2 : virtual public CClassA {
public: void fun_b() { cout << "fun_b:CClassB2\n"; }
};
class CClassC :virtual public CClassB1, virtual public CClassB2 {
public: void fun_c() { cout << "fun_b:CClassC\n"; }
};
int _tmain(int argc, _TCHAR* argv[]) {
CClassC obj;
obj.fun_a(); //right(虚继承(virtual),解决菱形继承引擎的二义性问题)
//obj.fun_b(); //error 虚继承不能解决这个不同作用域内产生函数重载的问题
obj.CClassB2::fun_b(); //right 所有的二义性问题都能这样解决
return 0;
}
重定义小结
- 派生类中构造函数与析构函数的执行顺序相反,先执行基类的构造函数再执行子类的构造函数。析构函数则先执行子类的析构函数,再执行基类的析构函数;
- 派生类的成员函数优先级别为先执行基类的成员函数,再执行子类的成员函数;
- 重定义的概念
重定义是子类需要修改或扩展基类的某个成员的功能时需要利用的机制;
重定义分别可以对基类的数据成员的重定义,或对基类成员函数的重定义;
重定义的新成员既可以与基类完全相同,也可以与基类成员函数名相同而参数不同;
- 1.不管子类重载的成员函数的参与与基类是否完全相同,都会构成重定义;
- 2.重定义是指在不同的作用域中定义的成员函数名相同,参数不同或相同的情况;
- 3.重定义的成员会覆盖掉其父类成员;
如果一个派生类有多个直接基类,而这些基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员,C++提供虚基类的方法,使得继承间接共同基类时只保留一份成员。
命名空间
引入了命名空间这个概念,是为了解决命名冲突的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。
命名空间是一个逻辑上的类型组织系统,用它来对程序中的类型进行逻辑上的分组,并使定义在同一个命名空间上的类可利用命名空间直接相互调用;
使用namespace关键字,将库或程序中的C++定义集封装在一个命名空间中,如果其他的定义中有相同的名字,但它们在不同的命名空间,则不会产生命名冲突;
定义命名空间
定义格式:
命名空间的定义使用关键字 namespace,后跟【命名空间的名称】
namespace 命名空间名
{
//命名空间成员(其他命名空间或类的定义)
}
使用格式:
为了调用带有命名空间的函数或变量,需要在前面加上命名空间的名称,如下所示:
#include "stdafx.h"
int main()
{
namespace Outer
{
int nNumA = 10;
int fun() {};
int fun1();
namespace Inner
{
int nNumA = 10;
int nNumC = 10;
int fun() {};
int fun1();
}
}
int Outer::fun1()
{
}
int Outer::Inner::fun1()
{
}
//1.通过名字单独访问
Outer::nNumA;
//2.通过声明特定的名字来访问
using Outer::fun;
fun();
//3.通过声明整个命名空间来访问
using namespace Outer;
nNumB;
fun1();
//=====内层命名空间,还必须单独在声明==============
//1.
Inner::nNumC;
//2.
using Inner::nNumC;
nNumC;
//3.通过声明整个命名空间来访问
using namespace Inner;
Inner::nNumC;
//==============================================
return 0;
}
C++学习6-面向对象编程基础(运算符重载、类的派生与继承、命名空间)的更多相关文章
- Java学习day7面向对象编程1-对象和类
一.Java编程中对象和类的概念 1,什么是类? 答:类是客观存在的,抽象的,概念的东西. 2,什么是对象? 答:对象是具体的,实际的,代表一个事物.例如:车是一个类,汽车,自行车就是他的对象. 关于 ...
- 大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础
第五章 函数式编程-基础5.1 函数式编程内容说明5.1.1 函数式编程内容5.1.2 函数式编程授课顺序5.2 函数式编程介绍5.2.1 几个概念的说明5.2.2 方法.函数.函数式编程和面向对象编 ...
- Python基础 — 面向对象编程基础
目录 1. 面向对象编程基础 2. 定义类和创建对象 3. init() 方法 4. 魔法方法 5. 访问可见性问题 5. 练习 1. 面向对象编程基础 把一组数据结构和处理它们的方法组成对象(obj ...
- [.net 面向对象编程基础] (6) 基础中的基础——运算符和表达式
[.net 面向对象编程基础] (6) 基础中的基础——运算符和表达式 说起C#运算符和表达式,小伙伴们肯定以为很简单,其实要用好表达式,不是一件容易的事.一个好的表达式可以让你做事半功倍的效果,比如 ...
- Python学习-第三天-面向对象编程基础
Python学习-第三天-面向对象编程基础 类和对象 简单的说,类是对象的蓝图和模板,而对象是类的实例.这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的 ...
- JAVA学习(五):Java面向对象编程基础
Java面向对象编程基础 面向对象(Object oriented programming,OOP)技术是一种强有力的软件开发方法,它採用数据抽象与信息隐藏技术,来使软件开发简单化,以达到代码重用的目 ...
- [.net 面向对象编程基础] (7) 基础中的基础——流程控制语句
[.net 面向对象编程基础] (7) 基础中的基础——流程控制语句 本来没有这一节的内容,后来考虑到既然是一个系列文章,那么就尽可能写的详细一些,本节参考了网上朋友所写的例子,为的是让更多小伙伴学习 ...
- [.net 面向对象编程基础] (1) 开篇
[.net 面向对象编程基础] (1)开篇 使用.net进行面向对象编程也有好长一段时间了,整天都忙于赶项目,完成项目任务之中.最近偶有闲暇,看了项目组中的同学写的代码,感慨颇深.感觉除了定义个类,就 ...
- [.net 面向对象编程基础] (14) 重构
[.net 面向对象编程基础] (14) 重构 通过面向对象三大特性:封装.继承.多态的学习,可以说我们已经掌握了面向对象的核心.接下来的学习就是如何让我们的代码更优雅.更高效.更易读.更易维护.当然 ...
- [.net 面向对象编程基础] (16) 接口
[.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目 ...
随机推荐
- 【刷题】LOJ 2863 「IOI2018」组合动作
题目描述 你在玩一个动作游戏.游戏控制器有 \(4\) 个按键,A.B.X 和 Y.在游戏中,你用组合动作来赚金币.你可以依次按这些按键来完成一个组合动作. 这个游戏有一个隐藏的按键序列,可以表示为由 ...
- adb 命令 链接 安装应用
adb connect 192.168.11.1:5555(可省略) adb kill-server 杀掉服务 adb install xxx.apk
- NOI 笔试题库(我背不住的部分)
吐槽 为什么C++选手要会编译Pascall啊!为什么Emacs选手要会使用Vim啊! Linux 中为文件改名使用的命令是:mv 在Linux 中删除当前目录下的test 目录的命令是:rm -r ...
- 洛谷 P4097 [HEOI2013]Segment 解题报告
P4097 [HEOI2013]Segment 题目描述 要求在平面直角坐标系下维护两个操作: 在平面上加入一条线段.记第 \(i\) 条被插入的线段的标号为 \(i\) 给定一个数 \(k\),询问 ...
- yum 安装Mysql
RHEL6.5-MySql-yum安装登录 客户端工具的使用mysql:Linux下提供了一个访问mysql服务器的客户端工具—mysql,其由mysql软件包提供,除了这些工具之外还有一些图形化界面 ...
- Hadoop、Hbase基本命令及调优方式
HDFS基本命令 接触大数据挺长时间了,项目刚刚上完线,趁着空闲时间整理下大数据hadoop.Hbase等常用命令以及各自的优化方式,当做是一个学习笔记吧. HDFS命令基本格式:Hadoop fs ...
- CF710F String Set Queries
CF710F String Set Queries 支持字符串的插入和删除...SAM也干不了这个事 所以可以用cdq分治+AC自动机O(nlogn)解决 但是本题强制在线~~~ 我们还有一个工具,叫 ...
- P1198 最大数 线段树水题
这道题模拟一下可以过,但是我们发现线段树也可以安全水过...... 写的线段树只需要滋磁单点修改,区间求max即可 我一开始犯了一个很SB的错误:每次插入修改了t,然后疯狂爆0到怀疑人生... 而且我 ...
- 多线程状态下调用SimpleDateFormat.format()抛出 ArrayIndexOutOfBoundsException 异常
本来想在类的顶部设置一个 静态的SimpleDateFormat常量 public final static DateFormat dateFormatGMT = new SimpleDateForm ...
- PHP依赖倒置和控制反转
判断代码的好坏,我们有自己的标准:高内聚,低耦合.为了解决这一问题,php中有许多优秀的设计模式,比如工厂模式,单例模式. 而在代码中体现出来的设计模式,就如依赖注入和控制反转. 那什么是依赖注入? ...