C++基础学习8:类的定义(class)
先来说说C和C++中结构体的不同
a) C语言中的结构体不能为空,否则会报错(??)
b) C语言中内存为空结构体分配大小为0,C++中为结构体和类分配大小为1byte
c) C语言中的结构体只涉及到数据结构,而不涉及到算法,也就是说在C中数据结构和算法是分离的。换句话说就是C语言中的结构体只能定义成员变量,但是不能定义成员函数。然而在C++中既可以定义成员变量又可以定义成员函数, C++中的结构体和类体现了数据结构和算法的结合。
不过虽然C语言的结构体中不能定义成员函数,但是却可以定义函数指针,不过函数指针本质上不是函数而是指针,所以总的来说C语言中的结构体只是一个复杂数据类型 ,只能定义成员变量,不能定义成员函数,不能用于面向对象编程。来看一个函数指针的例子:
int My_Add(int a, int b)
{
return a + b;
}
int My_Sub(int a, int b)
{
return a - b;
}
struct CTest
{
int(*Add)(int, int); // 函数指针
int(*Sub)(int, int);
}; int main()
{
struct CTest test;
int ret = ;
test.Add = My_Add;
test.Sub = My_Sub;
ret = test.Add(, );
printf("%d", ret);
}
c) 比如说这个结构体吧:
struct CTest
{
char ch;
int num;
}; int main()
{
CTest test;
test.num = ;
printf("%d", test.num);
} 这样在C语言中是编译不过去的,原因提示未定义标识符CTest。总的来说就是在C语言中结构体变量定义的时候,若为struct 结构体名 变量名定义的时候,struct不能省略。但是在C++之中则可以省略struct。
再来分析C++中的结构体与类的区别:
先来说说C++中两者的相同之处: 结构体中也可以包含函数;也可以定义public、private、protected数据成员;定义了结构体之后,可以用结构体名来创建对象。也就是说在C++当中,结构体中可以有成员变量,可以有成员函数,可以从别的类继承,也可以被别的类继承,可以有虚函数。总的一句话:class和struct的语法基本相同,从声明到使用,都很相似,但是struct的约束要比class多,理论上,struct能做到的class都能做到,但class能做到的stuct却不一定做的到。
再来说说两者的区别:对于成员访问权限以及继承方式,class中默认的是private,而struct中则是public。class还可以用于表示模板类型,struct则不行。 一、C++类的定义
C++中使用关键字 class 来定义类, 其基本形式如下:
class 类名
{
public:
//行为或属性
protected:
//行为或属性
private:
//行为或属性
};
只有在派生类中才可以通过派生类对象访问基类的protected成员。
示例:
定义一个点(Point)类, 具有以下属性和方法:
■ 属性: x坐标, y坐标
■ 方法: .设置x,y的坐标值; .输出坐标的信息。
实现代码:
class Point
{
public:
void setPoint(int x, int y);
void printPoint();
private:
int xPos;
int yPos;
};
代码说明:
上段代码中定义了一个名为 Point 的类, 具有两个私密属性, int型的xPos和yPos, 分别用来表示x点和y点。
在方法上, setPoint 用来设置属性, 也就是 xPos 和 yPos 的值; printPoint 用来输出点的信息。 数据抽象和封装
抽象是通过特定的实例抽取共同特征以后形成概念的过程。一个对象是现实世界中一个实体的抽象,一个类是一组对象的抽象。
封装是将相关的概念组成一个单元,然后通过一个名称来引用它。面向对象封装是将数据和基于数据的操作封装成一个整体对象,对数据的访问或修改只能通过对象对外提供的接口进行。
类定义
几个重要名词:
() 类名
遵循一般的命名规则; 字母,数字和下划线组合,不要以数字开头。
() 类成员
类可以没有成员,也可以定义多个成员。成员可以是数据、函数或类型别名。所有的成员都必须在类的内部声明。
没有成员的类是空类,空类也占用空间(1byte)。
class People
{
};
sizeof(People) = ;
() 构造函数
构造函数是一个特殊的、与类同名的成员函数,用于给每个数据成员设置适当的初始值。
() 成员函数
成员函数必须在类内部声明,可以在类内部定义,也可以在类外部定义。如果在类内部定义,就默认是内联函数。
类定义补充
3.1 可使用类型别名来简化类
除了定义数据和函数成员之外,类还可以定义自己的局部类型名字。
使用类型别名有很多好处,它让复杂的类型名字变得简单明了、易于理解和使用,还有助于程序员清楚地知道使用该类型的真实目的。
class People
{
public:
typedef std::string phonenum; //电话号码类型
phonenum phonePub; //公开号码
private:
phonenum phonePri; //私人号码
};
3.2 成员函数可被重载
可以有多个重载成员函数,个数不限。
3.3 内联函数
有三种:
()直接在类内部定义。
()在类内部声明,加上inline关键字,在类外部定义。
()在类内部声明,在类外部定义,同时加上inline关键字。注意:此种情况下,内联函数的定义通常应该放在类定义的同一头文件中,而不是在源文件中。这是为了保证内联函数的定义在调用该函数的每个源文件中是可见的。
3.4 访问限制
public,private,protected 为属性/方法限制的关键字。
3.5 类的数据成员中不能使用 auto、extern和register等进行修饰, 也不能在定义时进行初始化(C++11中支持)
如 int xPos = ; //错;
例外:
静态常量整型(包括char, bool)数据成员可以直接在类的定义体中进行初始化,例如:
static const int ia= ;
类声明与类定义
4.1 类声明(declare)
class Screen; 在声明之后,定义之前,只知道Screen是一个类名,但不知道包含哪些成员。只能以有限方式使用它,不能定义该类型的对象,只能用于定义指向该类型的指针或引用,声明(不是定义)使用该类型作为形参类型或返回类型的函数。
void Test1(Screen& a){};
void Test1(Screen* a){}; 4.2 类定义(define)
在创建类的对象之前,必须完整的定义该类,而不只是声明类。所以,类不能具有自身类型的数据成员,但可以包含指向本类的指针或引用。
class LinkScreen
{
public:
Screen window;
LinkScreen* next;
LinkScreen* prev;
}; //注意,分号不能丢
因为在类定义之后可以接一个对象定义列表,可类比内置类型,定义必须以分号结束:
class LinkScreen{ /* ... */ };
class LinkScreen{ /* ... */ } scr1,scr2;
类对象
定义类对象时,将为其分配存储空间。
Sales_item item; // 编译器分配了足以容纳一个 Sales_item 对象的存储空间。item 指的就是那个存储空间。
隐含的 this 指针
成员函数具有一个附加的隐含形参,即this指针,它由编译器隐含地定义。成员函数的函数体(成员变量也可以使用)可以显式使用 this 指针。 6.1 何时使用 this 指针
当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。最常见的情况是在这样的函数中使用 this:该函数返回对调用该函数的对象的引用。
class Screen
{
...
public:
Screen& set(char);
};
Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
类作用域
每个类都定义了自己的作用域和唯一的类型。
类的作用域包括:类的内部(花括号之内), 定义在类外部的成员函数的参数表(小括号之内)和函数体(花括号之内)。
class Screen
{
//类的内部
...
};
//类的外部
char Screen::get(index r, index c) const
{
index row = r * width; // compute the row location
return contents[row + c]; // offset by c to fetch specified character
}
注意:成员函数的返回类型不一定在类作用域中。可通过 类名::来判断是否是类的作用域,::之前不属于类的作用域,::之后属于类的作用域。例如
Screen:: 之前的返回类型就不在类的作用域,Screen:: 之后的函数名开始到函数体都是类的作用域。
class Screen
{
public:
typedef std::string::size_type index;
index get_cursor() const;
};
Screen::index Screen::get_cursor() const //注意:index前面的Screen不能少
{
return cursor;
}
该函数的返回类型是 index,这是在 Screen 类内部定义的一个类型名。在类作用域之外使用,必须用完全限定的类型名 Screen::index 来指定所需要的 index 是在类 Screen 中定义的名字。
========================================================================================
二 构造函数
构造函数是特殊的成员函数,用来保证每个对象的数据成员具有合适的初始值。
构造函数名字与类名相同,不能指定返回类型(也不能定义返回类型为void),可以有0-n个形参。
在创建类的对象时,编译器就运行一个构造函数。
构造函数可以重载
可以为一个类声明的构造函数的数量没有限制,只要每个构造函数的形参表是唯一的。
class Sales_item;
{
public:
Sales_item(const std::string&);
Sales_item(std::istream&);
Sales_item(); //默认构造函数
};
构造函数自动执行
只要创建该类型的一个对象,编译器就运行一个构造函数:
Sales_item item1("0-201-54848-8");
Sales_item *p = new Sales_item();
第一种情况下,运行接受一个 string 实参的构造函数,来初始化变量item1。
第二种情况下,动态分配一个新的 Sales_item 对象,通过运行默认构造函数初始化该对象。
构造函数初始化式
与其他函数一样,构造函数具有名字、形参表和函数体。
与其他函数不同的是,构造函数可以包含一个构造函数初始化列表:
Sales_item::Sales_item(const string &book) : isbn(book), units_sold(), revenue(0.0){ }
构造函数初始化列表以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。
构造函数可以定义在类的内部或外部。构造函数初始化只在构造函数的定义中指定。
构造函数分两个阶段执行:()初始化阶段;()普通的计算阶段。初始化列表属于初始化阶段(),构造函数函数体中的所有语句属于计算阶段()。
初始化列表比构造函数体先执行。不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化。
3.1 哪种类需要初始化式
const 对象或引用类型的对象,可以初始化,但不能对它们赋值,而且在开始执行构造函数的函数体之前要完成初始化。
初始化 const 或引用类型数据成员的唯一机会是构造函数初始化列表中,在构造函数函数体中对它们赋值不起作用。
没有默认构造函数的类类型的成员,以及 const 或引用类型的成员,必须在初始化列表中完成初始化。
class ConstRef
{
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
ConstRef::ConstRef(int ii)
{
i = ii; // ok
ci = ii; // error
ri = i; //
}
应该这么初始化:
ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) { }
3.2 成员初始化的次序
每个成员在构造函数初始化列表中只能指定一次。重复初始化,编译器一般会有提示。
成员被初始化的次序就是定义成员的次序,跟初始化列表中的顺序无关。
3.3 初始化式表达式
初始化式可以是任意表达式
Sales_item(const std::string &book, int cnt, double price) : isbn(book), units_sold(cnt), revenue(cnt * price) { }
3.4 类类型的数据成员的初始化式
初始化类类型的成员时,要指定实参并传递给成员类型的一个构造函数,可以使用该类型的任意构造函数。
Sales_item(): isbn(, ''), units_sold(), revenue(0.0) {}
3.5 类对象的数据成员的初始化
在类A的构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化。
类类型的数据成员,运行该类型的默认构造函数来初始化。
内置或复合类型的成员的初始值依赖于该类对象的作用域:在局部作用域中不被初始化,在全局作用域中被初始化为0。假设有一个类A,
class A
{
public:
int ia;
B b;
};
A类对象A a;不管a在局部作用域还是全局作用域,b使用B类的默认构造函数来初始化,ia的初始化取决于a的作用域,a在局部作用域,ia不被初始化,a在全局作用域,ia初始化0。 默认构造函数
不含形参的构造函数就是默认构造函数。
只要定义一个对象时没有提供初始化式,就使用默认构造函数。如: A a;
为所有形参提供默认实参的构造函数也定义了默认构造函数。例如:
class A
{
public:
A(int a=,char c =''){}
private:
int ia;
char c1;
}; 4.1 合成的默认构造函数
只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。
一个类只要定义了一个构造函数,编译器也不会再生成默认构造函数。
建议:
如果定义了其他构造函数,也提供一个默认构造函数。
如果类包含内置或复合类型(如 int& 或 string*)的成员,它应该定义自己的构造函数来初始化这些成员。每个构造函数应该为每个内置或复合类型的成员提供初始化。
隐式类类型转换
5.1 只含单个形参的构造函数能够实现从形参类型到该类类型的一个隐式转换
class A
{
public:
A(int a)
{
ia = a;
}
bool EqualTo(const A& a)
{
return ia == a.ia;
}
private:
int ia;
};
A a();
bool bEq = false;
bEq = a.EqualTo(); //参数为1,实现从int型到A的隐式转换
5.2 抑制由构造函数定义的隐式转换
通过将构造函数声明为 explicit,来防止在需要隐式转换的上下文中使用构造函数:
class A
{
public:
explicit A(int a )
{
ia = a;
}
bool EqualTo(const A& a)
{
return ia == a.ia;
}
private:
int ia;
};
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为 explicit。将构造函数设置为 explicit 可以避免错误。
三 复制控制
复制构造函数
1.1 几个要点
() 复制构造函数
复制构造函数是一种特殊构造函数,只有1个形参,该形参(常用 const &修饰)是对该类类型的引用。
class Peopel
{
public:
Peopel();//默认构造函数
Peopel(const Peopel&);//复制构造函数
~Peopel();//析构函数
};
当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。
Peopel a1; Peopel a2 = a1;
当将该类型的对象传递给函数或函数返回该类型的对象时,将隐式使用复制构造函数。
Peopel Func(Peopel b){...}
()析构函数
析构函数是构造函数的互补:当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。
析构函数可用于释放构造对象时或在对象的生命期中所获取的资源。
不管类是否定义了自己的析构函数,编译器都自动执行类中非 static 数据成员的析构函数。
() 复制控制
复制构造函数、赋值操作符和析构函数总称为复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。
() 两种初始化形式
C++ 支持两种初始化形式:直接初始化和复制初始化。直接初始化将初始化式放在圆括号中,复制初始化使用 = 符号。
对于内置类型,例如int, double等,直接初始化和复制初始化没有区别。
对于类类型:直接初始化直接调用与实参匹配的构造函数;复制初始化先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。直接初始化比复制初始化更快。
()形参和返回值
当形参或返回值为类类型时,由该类的复制构造函数进行复制。
()初始化容器元素
复制构造函数可用于初始化顺序容器中的元素。例如:
vector<string> svec();
编译器首先使用 string 默认构造函数创建一个临时值,然后使用复制构造函数将临时值复制到 svec 的每个元素。
()构造函数与数组元素
如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素。
如果使用常规的花括号括住的数组初始化列表来提供显式元素初始化式,则使用复制初始化来初始化每个元素。根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应元素:
Sales_item primer_eds[] = { string("0-201-16487-6"),
string("0-201-54848-8"),
string("0-201-82470-1"),
Sales_item()
}; 1.2 合成的复制构造函数
()合成的复制构造函数
如果没有定义复制构造函数,编译器就会为我们合成一个。
合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。
逐个成员初始化:合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。
例外:如果一个类具有数组成员,则合成复制构造函数将复制数组。复制数组时合成复制构造函数将复制数组的每一个元素。 1.3 定义自己的复制构造函数
() 只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数,也可以复制。
class Peopel
{
public:
std::string name;
unsigned int id;
unsigned int age;
std::string address;
};
() 有些类必须对复制对象时发生的事情加以控制。
例如,类有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。而另一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义自己的复制构造函数。
最好显式或隐式定义默认构造函数和复制构造函数。如果定义了复制构造函数,必须定义默认构造函数。
1.4 禁止复制
有些类需要完全禁止复制。例如,iostream 类就不允许复制。延伸:容器内元素不能为iostream
为了防止复制,类必须显式声明其复制构造函数为 private。(比如单例模式) 赋值操作符
与复制构造函数一样,如果类没有定义自己的赋值操作符,则编译器会合成一个。
()重载赋值操作符
Sales_item& operator=(const Sales_item &);
()合成赋值操作符
合成赋值操作符会逐个成员赋值:右操作数对象的每个成员赋值给左操作数对象的对应成员。除数组之外,每个成员用所属类型的常规方式进行赋值。对于数组,给每个数组元素赋值。
()复制和赋值常一起使用
一般而言,如果类需要复制构造函数,它也会需要赋值操作符。 析构函数
构造函数的用途之一是自动获取资源;与之相对的是,析构函数的用途之一是回收资源。除此之外,析构函数可以执行任意类设计者希望在该类对象的使用完毕之后执行的操作。
() 何时调用析构函数
• 撤销(销毁)类对象时会自动调用析构函数。
• 变量(类对象)在超出作用域时应该自动撤销(销毁)。
• 动态分配的对象(new A)只有在指向该对象的指针被删除时才撤销(销毁)。
• 撤销(销毁)一个容器(不管是标准库容器还是内置数组)时,也会运行容器中的类类型元素的析构函数(容器中的元素总是从后往前撤销)。
()何时编写显式析构函数
如果类需要定义析构函数,则它也需要定义赋值操作符和复制构造函数,这个规则常称为三法则:如果类需要析构函数,则需要所有这三个复制控制成员。
()合成析构函数
合成析构函数按对象创建时的逆序撤销每个非 static 成员,因此,它按成员在类中声明次序的逆序撤销成员。
对于每个类类型的成员,合成析构函数调用该成员的析构函数来撤销对象。
合成析构函数并不删除指针成员所指向的对象。 所以,如果有指针成员,一定要定义自己的析构函数来删除指针。 析构函数与复制构造函数或赋值操作符之间的一个重要区别:即使我们编写了自己的析构函数,合成析构函数仍然运行。
四 友元
友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。
友元可以出现在类定义的内部的任何地方。
友元不是授予友元关系的那个类的成员,所以它们不受声明出现部分的访问控制影响。
建议:将友元声明成组地放在类定义的开始或结尾。
友元类
class Husband
{
public:
friend class Wife;
private:
double money; //钱是老公私有的,别人不能动,但老婆除外
};
class Wife
{
public:
void Consume(Husband& h)
{
h.money -= ; //老婆可以花老公的钱
}
};
Husband h;
Wife w;
w.Consume(h);
使其他类的成员函数成为友元
class Husband; //1.声明Husband
class Wife //2.定义Wife类
{
public:
void Consume(Husband& h);
};
class Husband //3.定义Husband类
{
public:
friend void Wife::Consume(Husband& h); //声明Consume函数。
private:
double money; //钱是老公私有的,别人不能动,但老婆除外
};
void Wife::Consume(Husband& h) //4.定义Consume函数。
{
h.money -= ; //老婆可以花老公的钱
}
注意类和函数的声明和定义的顺序:
()声明类Husband
()定义类Wife,声明Consume函数
()定义类Husband
()定义Consume函数。
五 static 类成员
static 成员,有全局对象的作用,但又不破坏封装。
static 成员变量
static 数据成员是与类关联的对象,并不与该类的对象相关联。
static 成员遵循正常的公有/私有访问规则。 使用 static 成员而不是全局对象有三个优点。
() static 成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突。
() 可以实施封装。static 成员可以是私有成员,而全局对象不可以。
() 通过阅读程序容易看出 static 成员是与特定类关联的,这种可见性可清晰地显示程序员的意图。 static 成员函数
在类的内部声明函数时需要添加static关键字,但是在类外部定义函数时就不需要了。
因为static 成员是类的组成部分但不是任何对象的组成部分,所以有以下几个特点:
) static 函数没有 this 指针
) static 成员函数不能被声明为 const (将成员函数声明为 const 就是承诺不会修改该函数所属的对象)
) static 成员函数也不能被声明为虚函数 static 数据成员
static 数据成员可以声明为任意类型,可以是常量、引用、数组、类类型,等等。
static 数据成员必须在类定义体的外部定义(正好一次),并且应该在定义时进行初始化。
建议:定义在类的源文件中名,即与类的非内联函数的定义同一个文件中。注意,定义时也要带上类类型+"::"
double Account::interestRate = 0.035; 特殊的静态常量整型成员
静态常量整型数据成员可以直接在类的定义体中进行初始化,例如:
static const int period = ;
当然char 可以转换成整形,也是可以的, static const char bkground = '#';
六 其他 ()static 数据成员的类型可以是该成员所属的类类型。非 static 成员只能是自身类对象的指针或引用
class Screen
{
public:
// ...
private:
static Screen src1; // ok
Screen *src2; // ok
Screen src3; // error
};
()非 static 数据成员不能用作默认实参,static 数据成员可用作默认实参
class Screen
{
public:
Screen& clear(char = bkground);
private:
static const char bkground = '#'; //static const整型变量可以在类内部初始化。
};
C++基础学习8:类的定义(class)的更多相关文章
- 零基础学习python_类和对象(36-40课)
今天我们开始学习面向对象的知识咯,之前我对面向对象也学的懵懵的,因为感觉知道好像又不是特别清楚,接下来我们一起来学习类和对象吧.零基础的课程我都是看小甲鱼的视频学的,没基础的可以去这个网址下载视频学习 ...
- Ruby学习: 类的定义和实例变量
ruby是完全面向对象的,所有的数据都是对象,没有独立在类外的方法,所有的方法都在类中定义的. 一.类的定义语法 类的定义以 class 关键字开头,后面跟类名,以 end标识符结尾. 类中的方法以 ...
- python基础学习笔记——类的约束
⾸先, 你要清楚. 约束是对类的约束. 用一个例子说话: 公司让小明给他们的网站完善一个支付功能,小明写了两个类,如下: class QQpay: def pay(self,money): print ...
- C++ template学习二 类模板定义及实例化
一个类模板(也称为类属类或类生成类)允许用户为类定义一种模式,使得类中的某些数据成员.默写成员函数的参数.某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的). 如果一个类中数据成员 ...
- Java基础学习-Random类和Java数组
1.随机数类(Random) package com.denniscui; import java.util.Random; /* * Random:用于产生随机数 * * 使用步骤: * ...
- python基础学习笔记——类的成员
一. 细分类的组成成员 之前咱们讲过类大致分两块区域,如下图所示: 每个区域详细划分又可以分为: class A: company_name = '老男孩教育' # 静态变量(静态字段) __ipho ...
- Java基础学习-基本数据类型变量的定义和使用
注意:如果使用notepad++编码,在cmd控制台编译时报错(编码GBK的不可映射字符),可以参考如下链接进行设置:https://jingyan.baidu.com/article/e3c78d6 ...
- 学而精计算机公共基础学习之路TEST2(程序设计基础)
程序设计基础 程序设计方法与风格 1.程序设计方法 程序设计: 指设计.编制.调试程序的方法和过程. 程序设计方法是研究问题求解如何进行系统构造的软件方法学.常用的程序设计方法有:结构化程序设计方法. ...
- Java基础学习笔记六 Java基础语法之类和ArrayList
引用数据类型 引用数据类型分类,提到引用数据类型(类),其实我们对它并不陌生,如使用过的Scanner类.Random类.我们可以把类的类型为两种: 第一种,Java为我们提供好的类,如Scanner ...
- C++学习4-面向对象编程基础(面向对象概念,定义类,定义对象)
什么是面向对象? 在软件的设计过程中的两种方式: 把程序按照算法的执行步骤来拆解,一步步实现,这是面向过程编程: 把程序按照现实世界的理解,分成不同对象,通过多个对象之间的相互作用,来完成程序的最终功 ...
随机推荐
- Windows_Server_2008远程桌面多用户登陆的配置方法
开启远程桌面后,Windows Vista(或Windows 2008)下默认只支持一个administrator用户登陆,一个登录后另一个就被踢掉了,下面提供允许同一个用户名同时多个用户登录的配置方 ...
- Celery-4.1 用户指南: Extensions and Bootsteps (扩展和Bootsteps)
自定义消息消费者 你可能想要嵌入自定义的 Kombu 消费者来手动处理你的消息. 为了达到这个目的,celery 提供了一个 ConsumerStep bootstep 类,你只需要定义 get_co ...
- react-router4.x 实用例子(路由过渡动画、代码分割)
react-router4.2.0实用例子 代码分割 官网上面写的代码分割是不支持create-react-app脚手架的,要使用import实现 创建一个bundle.js文件 import { C ...
- 获取Linux权限后安装rootkit
1.首先获得远程服务器的root权限,当然这是基本的也是最难的. 2.然后下载rootkit程序,本文用到的是mafix. 3.开始安装 wget http://godpock.googlecode. ...
- C#高级参数params的使用
params,可变参数,使用十分简单,看代码吧. using System; using System.Collections.Generic; using System.Linq; using Sy ...
- 4-4 zk特性 – 理解watcher机制
watcher是zk里面非常重要的特性.watcher一定要去好好地看一下,一定要去好好地理解一下它是如何去用的,包括触发的事件类型等等.监督者也可以理解为触发器,也就是说当我们的节点发生了一些变化的 ...
- 34- 24 Point game
http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=43 24 Point game 时间限制:3000 ms | 内存限制:65535 KB ...
- ElasticSearch 入门(转)
最大的特点: 1. 数据库的 database, 就是 index 2. 数据库的 table, 就是 tag 3. 不要使用browser, 使用curl来进行客户端操作. 否则会出现 jav ...
- Luogu 2824 [HEOI2016/TJOI2016]排序
BZOJ 4552 挺妙的解法. 听说这题直接用一个桶能拿到$80 \ pts$ 发现如果是一个排列的话,要对这个序列排序并不好做,但是假如是$01$序列的话,要对一个区间排序还是很简单的. 发现最后 ...
- Oracle 系统表大全
数据字典dict总是属于Oracle用户sys的. 1.用户: select username from dba_users; 改口令 alter user spgroup identified by ...