面向对象的 C++ 11 程序设计语言

2011 年,C++ 标准委员会发布了 ISO C++ 标准的一个重要修订版 C++ 11;该修订版是 C++ 语言演进过程当中的重要一步,也是当前获得编译器(GCCLLVMQt5Visual C++支持较多,兼容性最为优秀的一个版本。增添了类型说明符autodecltypeLambda 表达式智能指针 unique_ptr shared_ptr weak_ptr空指针 nullptr等诸多新特性,语言风格更加灵活统一的同时,极大提升了程序的编写效率。

本文基于《C++ Primer》一书最新的第 5 版撰写而成,该书作为 C++ 语言学习的经典读物,同样与时俱进增添了 C++ 11 的诸多新特性。因此,本文也选择了支持 C++ 11 标准的 Qt5 作为开发编译环境。由于 C++ 面向过程的语法与 C 语言类似,而笔者之前已经在《Linux C 标准程序设计》一文对相关内容进行了详尽的表述,因而本文将会着重笔墨水介绍 C++ 面向对象以及标准库方面的内容。

输入输出流

C++ 语言并未定义任何输入输出语句,而是通过附加的iostream标准库提供 IO 机制,该库包含输入流istream输出流ostream两种基本类型。(Stream)本质上是一个随着时间推移,顺序生成或消耗的字符序列。iostream标准库当中一共定义了 4 个 IO 对象:

  • cin: 标准输入流(istream类型);
  • cout:标准输出流(ostream类型);
  • cerr:标准错误流(ostream类型);
  • clog:标准日志流(ostream类型);
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int main(int argc, char *argv[]) {
int input1 = 0, input2 = 0; // 声明和于接收用户输入的 input1 input2 变量

std::cout << "Enter two numbers : " << std::endl; // 提示用户输入 2 个数值
std::cin >> input1 >> input2; // 使用上面定义的变量接收用户输入

std::cerr << "input1 with cerr is " << input1 << std::endl; // 标准错误流输出 input1 input2 变量值
std::clog << "input2 with clog is " << input2 << std::endl; // 标准日志流输出 input1 input2 变量值

return 0;
}

注意:如果需要通过控制台打印中文信息,那么可以将 Qt Creator 的文本编码设置为【System】。

上述代码当中,endl是一个称为操纵符的特殊值,主要用于结束当前行的输出,并将缓冲区(buffer)里的内容刷新至显示设备,这种刷新操作可以确保将程序产生的所有输出信息都写入至输出流。

命名空间(namespace)可以避免代码中出现命名冲突,cincoutcerrclogendl等标准库成员都定义在名为std命名空间当中,因此代码里需要使用作用域运算符::显式的指定命名空间。

此外,输出运算符<<用于将右侧的输出值写入到左侧的ostream对象当中,并返回写入内容的ostream对象,从而形成链式调用。而输入运算符>>正好相反,将右侧的输入值写入到左侧的istream对象当中,然后同样将该对象返回以实现链式调用。

命名空间

命名空间用于区分不同作用域当中相同名称的变量、函数或者类,定义命名空间需要使用namespace关键字,后面紧跟命名空间的名称:

1
2
3
namespace 命名空间名称 {
// 声明变量、函数、类
}

然后,通过命名空间名称::变量/函数/类即可调用带有命名空间的变量、函数以及类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <string>
#include <iostream>

/* 使用 namespace 关键字声明命名空间 */
namespace Hank {
std::string birthday= "2020";

void sayHello(){
std::cout << "Hello World" << std::endl;
};

class Person {
public:
std::string name;
int age;
};
}

int main (int argc, char *argv[]) {
/* 使用命名空间中的变量 */
std::cout<< Hank::birthday << std::endl;

/* 使用命名空间中的函数 */
Hank::sayHello();

/* 使用命名空间中的类 */
Hank::Person person;
person.name = "Hank";
person.age = 35;
std::cout<< person.name << "-" << person.age << std::endl;
return 0;
};
1
2
3
2020
Hello World
Hank-35

上述代码分别使用了标准库定义的std和自定义的Hank两个命名空间,这种方式需要为所有变量都添加专属的命名空间名称,这样编写代码显得较为繁琐。面对这种情况,可以使用更为便捷的using关键字指定后续代码所处的命名空间。

1
using namespace 命名空间名称;

下面的代码,在开头处声明了using namespace std;,这样在使用coutendl时就无需再分别添加std::前缀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

/* 声明当前采用的命名空间 */
using namespace std;

int main (int argc, char *argv[]) {
char hello[] = "Hello Qt5! Hello ";
char name[5];

cin >> name;
cout << hello << name << endl;

return 0;
};

针对每条using可以只引入命名空间当中的一个成员,例如将std标准库当中的名字全部通过using关键字声明出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

/* 对命名空间中的名字展开进行声明 */
using std::cout;
using std::cin;
using std::endl;

int main (int argc, char *argv[]) {
char hello[] = "Hello Qt5! Hello ";
char name[5];

cin >> name;
cout << hello << name << endl;

return 0;
};

上述代码声明命名空间以后,再使用cincoutendl时就不需要再添加std::前缀。

注意:位于头文件的代码通常不应该使用using声明。因为头文件的内容会拷贝到所有引用它的源文件当中,导致头文件里的using声明,与源文件的命名空间发生冲突。

命名空间可以由几个单独定义的部分构成,甚至一个命名空间的各个组成部分可以分散在多个源文件当中。如果命名空间中的某个构成部分需要使用定义在其它源文件里的成员,则仍然需要在当前源文件声明该命名空间。除此之外,命名空间还可以进行嵌套,即可以在一个命名空间中定义另外的命名空间:

1
2
3
4
5
6
7
8
9
namespace 命名空间1 {
// 代码声明
namespace 命名空间2 {
// 代码声明
}
}

using namespace 命名空间1; // 访问命名空间 1 里的成员
using namespace 命名空间1::命名空间2; // 访问命名空间 2 里的成员

下面是一个嵌套命名空间的完整示例,声明using namespace Space1::Space2;以后最终调用的是Space2命名空间中的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

namespace Space1{
void func(){
cout << "Space1" << endl;
}
namespace Space2{
void func(){
cout << "Space2" << endl;
}
}
}

using namespace Space1::Space2;

int main () {
func(); // 调用 Space2 命名空间中的 func()
return 0;
}
1
Space2

数据类型

C++ 11 当中出现的数据类型,基本与 C99 标准保持一致,例如两者都兼容long long数据类型,C++ 11C99 中需要通过#include <stdbool.h>支持的_Bool升级为原生的bool关键字,并额外提供了wchar_tchar16_tchar32_t三种针对字符集编码的全新数据类型。

类型 名称 占用存储空间
bool 布尔型 1 bytes
char 字符型 1 bytes
wchar_t 宽字符型 2 bytes
char16_t Unicode 字符型 2 bytes
char32_t Unicode 字符 4 bytes
short 短整型 2 bytes
int 整型 4 bytes
long 长整型 4 bytes
long long 长长整型 8 bytes
float 单精度浮点型 4 bytes
double 双精度浮点型 8 bytes
long double 长双精度浮点型 12 bytes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

using namespace std;

int main () {
cout << "bool " << sizeof(bool) << " bytes" << endl;
cout << "char " << sizeof(char) << " bytes" << endl;
cout << "wchar_t " << sizeof(wchar_t) << " bytes" << endl;
cout << "char16_t " << sizeof(char16_t) << " bytes" << endl;
cout << "char32_t " << sizeof(char32_t) << " bytes" << endl;
cout << "short " << sizeof(short) << " bytes" << endl;
cout << "int " << sizeof(int) << " bytes" << endl;
cout << "long " << sizeof(long) << " bytes" << endl;
cout << "long long " << sizeof(long long) << " bytes" << endl;
cout << "float " << sizeof(float) << " bytes" << endl;
cout << "double " << sizeof(double) << " bytes" << endl;
cout << "long double " << sizeof(long double) << " bytes" << endl;

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
bool         1  bytes
char 1 bytes
wchar_t 2 bytes
char16_t 2 bytes
char32_t 4 bytes
short 2 bytes
int 4 bytes
long 4 bytes
long long 8 bytes
float 4 bytes
double 8 bytes
long double 12 bytes

注意整型字符型还可以使用signed(有符号类型)、unsigned(无符号类型)修饰符,表示该变量的二进制存储形式是否采用符号位

布尔类型 bool

C++ 里的布尔类型只有truefalse两个可选值,其底层本质上保存的分别是整型数据10,但是仅占用 1 个字节的存储空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

using namespace std;

int main () {
bool no = false;
bool yes = true;

cout << (bool)no << endl;
cout << (bool)yes << endl;

return 0;
};
1
2
0
1

宽字符类型 wchar_t

宽字符类型wchar_t用于表达宽字符串(例如:日文系统字符集),其与char类型一样底层存储的是整型数据,但拥有更大的存储空间,进而可以表达更多的字符编码。代码中,可以通过前缀L来表示宽字符常量和宽字符串,下面代码将字母H和单词Hankwchar_t类型进行输出:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

using namespace std;

int main () {
wchar_t wc = L'H';

wcout << wc << endl;
wcout << L"Hank" << endl;

return 0;
}
1
2
H
Hank

Unicode 类型 char16_t 和 char32_t

C++ 11 新增的char16_tchar32_t数据类型可以用于表达更加庞大的 Unicode 字符集(UTF-16、UTF-32)。其中,char16_t长度为 16 位,char32_t长度为 32 位,两者都是无符号的。C++ 11 使用小写字母u作为前缀表示char16_t类型的字符或者字符串常量,例如:u'A'或者u"Hank";使用大写字母U作为前缀表示char32_t类型的字符或者字符串常量,例如:U'B'或者U"uinika"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

using namespace std;

int main (int argc, char *argv[]) {
char16_t char16 = u'A';
char16_t string16[] = u"Hank";
cout << (char)char16 << endl;
cout << (char)string16[0] << endl;

char32_t char32 = U'B';
char32_t string32[] = U"Uinika";
cout << (char)char32 << endl;
cout << (char)string32[0] << endl;

return 0;
}
1
2
3
4
A
H
B
U

string 类

ISO/ANSI C++98 标准添加了string类对 C++ 库进行了扩展,因此可以使用string对象类型的变量而非字符数组来存储字符串。使用string类时,必须在程序中包含头文件#include <string>,且由于string类位于std命名空间,因而还需要额外声明using namespace std,或者直接采用std:string方式进行引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>

using namespace std;

int main (int argc, char *argv[]) {
string hello1 = "Hello Qt! "; // 直接赋值方式初始化字符串
string hello2("Hello Hank! "); // 构造函数方式初始化字符串
string hello3(5, 'U'); // 将变量 hello3 初始化为由 5 个连续字符 U 组成的字符串

cout << hello1 << hello2 << hello3 << endl;

return 0;
};
1
Hello Qt! Hello Hank! UUUUU

string类型不能像 Java 那样使用+运算符进行拼接,而需要通过调用string类的append()函数来完成。接下来修改代码,以append()方式实现与上面代码相同的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>

using namespace std;

int main (int argc, char *argv[]) {
string hello1 = "Hello Qt! ";
string hello2("Hello Hank! ");
string hello3(5, 'U');

cout << hello1.append(hello2).append(hello3) << endl; // 使用 append() 拼接字符串

return 0;
};

运算符==!=分别用于检验string对象的相等性,两个string对象相等意味着其长度和包含的字符都相同。关系运算符<<=>>=则用于检验string对象的字典顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>

using namespace std;

int main (int argc, char *argv[]) {
string A = "Hank";
string B = "Uinika";

cout << (A != B) << endl;
cout << (A == B) << endl;
cout << (A <= B) << endl;

return 0;
};
1
2
3
1
0
1

注意:标准输出中可以使用boolalpha为字符串流设置boolalpha格式标记,让布尔值能以true或者false的格式进行显示,而noboolalpha则用于清除字符串流当中的boolalpha格式标志;

函数

内联函数

C++ 内联函数能够让编译器使用相应的函数代码替换函数调用,这样程序无需跳转至另一个函数位置执行代码,然后再跳转回调用位置,这样内联的函数执行速度会相对更快。使用时需要在声明和定义函数时都添加inline关键字。

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <string>

using namespace std;

inline string hello(){ return "Hello Qt!"; }

int main (int argc, char *argv[]) {
cout << hello() << endl;

return 0;
}
1
Hello Qt!

注意:当开发人员显式请求将函数作为内联函数时,编译器并不一定会满足这种要求,它可能认为该函数过大或是存在递归操作(内联函数不能递归),因而不将其视为内联函数。

默认参数

C++ 函数的默认参数是指当函数调用中省略了实际参数时使用的一个默认值,这样可以极大提高函数使用的灵活性。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

using namespace std;

int sum(int a = 2020, int b = 1985) {
return a + b;
}

int main (int argc, char *argv[]) {
cout << "Total value is : " << sum() << endl;
cout << "Total value is : " << sum(1 , 2) << endl;
return 0;
}
1
2
Total value is : 4005
Total value is : 3

函数重载

同一作用域当中,函数名称相同,参数个数或者类型不同的函数称为函数重戴

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>

using namespace std;

string print(string parameter) {
return "Hello " + parameter + "!";
}

string print(string parameter1, string parameter2) {
return "Hello " + parameter1 + " and " + parameter2 + "!";
}

int main (int argc, char *argv[]) {
cout << print("Qt") << endl;
cout << print("Hank", "Uinika") << endl;
return 0;
}

注意main()函数不能进行重载。

泛型参数

通过参数泛型来定义函数,泛型参数可以让编译器自动生成该类型参数对应的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

using namespace std;

/* 声明函数参数泛型 */
template <typename Type>

/* 定义接收泛型参数的函数 */
void display(Type parameter){
cout << parameter << endl;
}

int main (int argc, char *argv[]) {
display("Qt"); // 传入字符串参数
display(2020); // 传入整型参数

return 0;
}

异常处理

  • try:侦测代码异常;
  • catch:捕获并处理异常;
  • throw:手动抛出异常;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

double division(int a, int b) {
if( b == 0 ) {
throw "Zero cannot be used as a divisor!";
}

return (a/b);
}

int main (int argc, char *argv[]) {
int x = 1985;
int y = 0;
int result;

try {
result = division(x, y);
}catch (const char* message) {
cerr << message << endl;
}

return 0;
}

注意:C++ 并未提供finally子句,而是采用类的析构函数来进行对象的销毁工作。

C++ 类

C++ 以为核心实现了面向对象程序设计,而类本质上是一种抽象数据类型(Abstract Data Type),包含了数据(成员变量)以及数据的处理方法(成员函数)。类的定义需要使用关键字class,后面跟随类的名称,类的主体是包含在一对花括号{}当中,最后以一个分号;结尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <string>
#include <iostream>

using namespace std;

class Person{
public:
/* 成员变量 */
string name; // 姓名
float height; // 身高
float weight; // 体重
/* 成员函数 */
void print() {
cout<< name << endl << height << endl << weight <<endl;
}
};

int main(int argc, char *argv[]) {
/* person1 */
Person person1;
person1.name = "Hank";
person1.height = 182.5;
person1.weight = 76.3;
person1.print();

/* person2 */
Person person2;
person2.name = "Trump";
person2.height = 190;
person2.weight = 110;
person2.print();

return 0;
}
1
2
3
4
5
6
Hank
182.5
76.3
Trump
190
110

类的成员函数即可以定义在类的内部,也可以借助范围解析运算符::定义在类的外部(但是函数声明需要放置在类内部):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <string>
#include <iostream>

using namespace std;

class Person{
public:
string name; // 姓名
float height; // 身高
float weight; // 体重
void print(); // 声明类外部的成员函数
};

/* 使用范围解析运算符 :: 在类外部定义成员函数 */
void Person::print() {
cout<< name << endl << height << endl << weight <<endl;
}

int main(int argc, char *argv[]) {
Person person;
person.name = "Hank";
person.height = 182.5;
person.weight = 76.3;
person.print();

return 0;
}
1
2
3
Hank
182.5
76.3

访问修饰符

访问修饰符publicprivateprotected用于约束各个类成员的访问限制,每个类可以使用多个访问修饰符,每个访问修饰符 作用于下一个访问修饰符出现之前,当类的成员没有修饰符时则默认为private

1
2
3
4
5
6
7
8
9
10
class Base {
public:
/* public 成员 */

private:
/* private 成员 */

protected:
/* protected 成员 */
};

public

public成员在类的外部被访问,无需使用其它成员函数来设置set()和获取get()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <string>
#include <iostream>

using namespace std;

class Person{
public:
string name; // 姓名
void print(); // 声明类外部的成员函数
};

void Person::print() {
cout<< name << endl;
}

int main(int argc, char *argv[]) {
Person person;
person.name = "Hank";
person.print();

return 0;
}
1
Hank

private

private成员在类的外部不允许被访问,如果直接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <string>
#include <iostream>

using namespace std;

class Person{
private:
string name; // 姓名
public:
void print();
};

void Person::print() {
cout<< name << endl;
}

int main(int argc, char *argv[]) {
Person person;
person.name = "Hank";
person.print();

return 0;
}
1
2
3
C:\Workspace\test\main.cpp:20: error: 'std::__cxx11::string Person::name' is private within this context
person.name = "Hank";
^~~~

通常,需要借助于settergetter方法来存取采用了private修饰符的成员变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <string>
#include <iostream>

using namespace std;

class Person{
private:
string name; // 使用 private 修饰符
public:
string getName(); // Getter
void setName(string target); // Setter
};

void Person::setName(string target) {
name = target;
}
string Person::getName() {
return name;
}

int main(int argc, char *argv[]) {
Person person;
person.setName("Hank");
cout << person.getName() << endl;

return 0;
}
1
Hank

protected

protected成员与private的特性类似,但是可以在其所在类的子类当中进行访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <string>
#include <iostream>

using namespace std;

class Person{
protected:
string name; // 使用 protected 修饰符
};

/* Hank 是 Person 的子类 */
class Hank:Person {
public:
string getName(); // Getter
void setName(string target); // Setter
};

void Hank::setName(string target) {
name = target;
}
string Hank::getName() {
return name;
}

int main(int argc, char *argv[]) {
Hank hank;
hank.setName("Hank");
cout << hank.getName() << endl;

return 0;
}
1
Hank

构造函数

类的构造函数会在每次创建类的新对象时被执行,可用于为成员变量赋初始值,构造函数名称与类名称完全相同,而且不会返回任何类型(包括void)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

using namespace std;

class Hank {
public:
string name = "Hello C++ !";
/* 定义构造函数 */
Hank() {
cout<< name << endl;
}
};

int main() {
Hank hank;
return 0;
}
1
Hello C++ !

构造函数同样可以采用范围解析运算符::定义在类的外部,而且可以携带上参数,为的成员变量进行赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace std;

class Hank {
private:
string name;

public:
Hank(string name);
};

Hank::Hank(string name) {
cout<<name<<endl;
this->name = name;
}

int main() {
Hank hank("Hello C++ !");
return 0;
}

注意:构造函数访问修饰符即可以是public,也可以是private(只有该类的成员函数才能构造该对象)或者protected(无法在类的外部构造该对象,只能构造该类的子类)。

析构函数

析构函数名称是在类名称前面添加了 1 个波浪号~前缀,即没有任何返回值,也不能携带任何参数,通常用于关闭、清理、释放资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

using namespace std;

class Hank {
private:
string name;

public:
/* 构造函数定义 */
Hank(){
cout<<"Constructor"<<endl;
};
/* 析构函数定义 */
~Hank(){
cout<<"Destructor"<<endl;
};
};

int main() {
Hank hank;
return 0;
}
1
2
Constructor
Destructor

析构函数同样可以通过范围解析运算符::被定义在类的外部,改写上面的示例代码,将析构函数的定义移动到类的外部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

using namespace std;

class Hank {
private:
string name;

public:
/* 构造函数定义 */
Hank(){
cout<<"Constructor"<<endl;
};
~Hank();
};

/* 析构函数定义 */
Hank::~Hank(){
cout<<"Destructor"<<endl;
};

int main() {
Hank hank;
return 0;
}

this 指针

每个 C++ 对象都能通过this指针访问自身的十六进制地址,所有类的成员函数都可以通过引用this指针来指向当前调用的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <string>
#include <iostream>

using namespace std;

class Test{
public:
string name;
void address() {
cout<< this << endl; // 获取 this 指针
}
};

int main(int argc, char *argv[]) {
Test test;
test.address();
cout<< &test << endl; // 获取类实例地址

return 0;
}
1
2
0x61fea8
0x61fea8

注意:友元函数没有this指针,因为友元并非类的成员,仅有成员函数才拥有this指针。

通过指针访问 C++ 结构体或者的成员,需要使用专门的成员访问运算符->。下面代码中,无论是通过this指针,还是通过*声明的指针变量,访问成员变量和方法时,都需要采用->运算符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <string>
#include <iostream>

using namespace std;

class Test{
public:
string name;
void address() {
cout<< this->name << endl; // 通过 this 指针访问成员变量
}
};

int main(int argc, char *argv[]) {
Test test;
test.name = "Hello Qt!";

Test *test_pointer = &test; // 通过指针变量访问成员函数
test_pointer->address();

return 0;
}

static 静态成员

C++ 类当中声明为static的称为静态成员,静态成员属于类,而非属于对象。静态成员可以通过类名和范围解析运算符::直接调用,而无需将类进行实例化。静态成员函数只能访问其它静态的成员变量和成员函数,并且不能够访问对象的this指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <string>
#include <iostream>

using namespace std;

class Demo{
public:
/* 静态成员变量 */
static string variable;
/* 静态成员函数 */
static void function(){
cout << variable << endl;
}
};

string Demo::variable = "Variable"; // 初始化静态成员变量

int main(int argc, char *argv[]) {
Demo::function(); // 调用静态成员函数

return 0;
}
1
Variable

通常情况下,类的静态成员不应该在类的内部进行初始化,但是可以为声明了constexpr常量表达式类型的静态成员提供整型初始值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <string>
#include <iostream>

using namespace std;

class Demo{
public:
/* 常量表达式类型的整型初始值 */
static constexpr int variable = 32;
};

int main(int argc, char *argv[]) {
cout << Demo::variable << endl; // 输出静态成员变量的值
return 0;
}

friend 友元

面向对象的 C++ 11 程序设计语言

http://www.uinio.com/C&C++/C++/

作者

Hank

发布于

2018-06-21

更新于

2018-07-31

许可协议