CPP-Basic

前言

CPPBasic

C的区别

1 如果函数没有参数,C语言建议使用void,而C++建议不写。
2 C语言中用结构体定义变量时,“struct 结构体名 变量名”,在C++中“struct”可以省略。
3 C语言的控制台输入输出是用函数来实现的,最常用的是printf()和scanf()函数。C++语言中一般使用运算符“<<”和“>>”,这比C语言中的函数的功能更强大、更方面,下面只介绍基本使用,更强大的功能要等学完了“类”之后才讲解,这样更容易理解。

C语言中运算符“<<”和“>>”是位操作运算符,C++中仍然有效,而用作输入出时,最前面必须是cin或cout,表示从控制台输入或输出,cin和cout是C++定义好的输入出流。输出时当需要回车时,可以用“\n”,也可以用“endl”。

缺省指和重载

在C++中参数可以设置缺省值,设置了缺省值之后,这个参数在调用时可以省略。
注意:设置缺省值的参数只能是最后的几个参数。

函数的重载在C++中具有重要作用,重载(overload)不要和后面要学的覆盖(override)混淆起来。重载是指相同的函数名,但参数的类型或个数中只要有所不同,便进行再定义,编译之后重载的函数都具有不同的地址,也就是说虽然函数名相同,实际上是不同的函数,在调用时,编译系统会根据不同之处自动区别是调用哪一个函数。对于普通函数和后面要学的类的成员函数都适用。

覆盖(重写)只对类的构造函数或成员函数适用,是子类继承父类是才使用的非常有用的功能。重载可以针对运算符,而覆盖不行。上一节中缺省值的设置,实际上在编译时等价于重载,被生成了4个不同的函数。

重载函数的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
void space(int count);
void space(int count, char ch);
int main ( )
{
//有一个整型参数的函数指针
void (*fp1)(int);
//有一个整型参数、一个字符型参数的函数指针
void (*fp2)(int, char);
fp1 = space; //取space(int)函数的地址
fp2 = space; //取space(int, char)函数的地址
fp1(20); //输出20个空格
cout << "|" << endl;
fp2(20, '='); //输出20个等号
cout << "|" << endl;
return 0;
}

引用

“引用(reference)”与指针象似但不是指针。引用主要有3种用法:

  • 单独使用(一般称为“独立引用”);
  • 作参数使用;
  • 作返回值使用。
    从功能上来说,引用型变量又可以看做事被引用变量的“别名”,这两个变量只是名称不同,变量的地址是同一个(共用体中的元素也是一样)。使用“引用”的好处主要是可以“逆向引用”。

常量也可以被引用,例如:“const int &ref = 10;”,这也是正确的,但这样定义无任何意义。定义独立引用时需要注意以下规则:
引用型变量在定义时必须初始化。 被引用的对象必须已经分配了空间,即不能为空,或空指针。
*被引用的对象不能为地址,即指针变量、数组变量等不能被引用。

生成实例的3种方法

了解生成实例的三种方法的细微区别是很重要的:
申明为变量; 从无名对象复制;
*申明为指针并动态生成。
注意:指针的成员用“->”,而不用“.”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include <iostream>
#include <string>

using namespace std;
int main ( )
{
string strA("C语言中文网"); //直接调用构造函数生成实例
cout << strA << " " << strA.length() << endl;
string strB; //先调用构造函数生成空字符串实例
strB = string("C++60分钟入门教程"); //再调用构造函数生成无名实例,然后复制给strB实例,无名实例立即销毁
cout << strB << " " << strB.length() << endl; //这和上面①的方法的结果相同
string *strC; //先定义一个指针,尚未分配空间
//动态调用构造函数生成实例后,再将实例地址赋给指针变量
strC = new string("http://see.xidian.edu.cn/cpp/biancheng/cpp/jichu/");
cout << *strC << " " << strC->length() << endl;
delete strC; //千万不要忘记释放
return 0;
}

成员函数和运算符重载

构造函数的重载

构造函数可以重载,使得生成实例时非常方便。构造函数一般要对成员变量赋初值,有两种写法:

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
#include <iostream>
#include <string>
using namespace std;
class stuff {
string name;
int age;
public:
stuff() { //这是写法一
cout << name << "---" << age << endl;
name = "空";
age = 0;
cout << name << "---" << age << endl;
}
stuff(string n, int a):name(n),age(a) //这是写法二
{
cout << name << "---" << age << endl;
}
string getName() {
return name;
}
int getAge() {
return age;
}
};
int main ( )
{
stuff st2;
stuff st1("C语言中文网", 3);
return 0;
}

运行结果:
—2817728
空—0
C语言中文网—3
写法一是在构造函数体中赋值,赋值前成员变量已经有了地址空间,尚未有值。写法二是一种特殊方法,是在成员变量分配空间的同时将参数的值赋给成员变量。虽然写法二用的人少,但明显优于写法一。

运算符重载

运算符重载对于普通函数和成员函数来说,格式稍有不同。

//单目运算符
成员函数: 返回值类型 operator 运算符 () ;
普通函数: 返回值类型 operator 运算符 (对象的类型) ;

//双目运算符
成员函数: 返回值类型 operator 运算符 (对象的类型) ;
普通函数: 返回值类型 operator 运算符 (对象的类型1, 对象的类型2) ;

//函数调用
成员函数: 返回值类型 operator (任意的参数列) ;

//数组元素
成员函数: 返回值类型 operator[] (参数类型) ;

//增1/减1运算符
成员函数: 返回值类型 operator 运算符 (int) ;
普通函数: 返回值类型 operator 运算符 (对象的类型, int) ;

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
35
36
#include <iostream>
#include <string>
using namespace std;
class stuff {
string name;
int age;
public:
stuff(string n, int a):name(n),age(a)
{
cout << name << "---" << age << endl;
}
string getName() {
return name;
}
int getAge() {
return age;
}
void operator +(int x); //运算符重载的定义
void operator +(string s); //运算符重载的定义
};
void stuff::operator +(int x) //运算符重载的实装
{
age = age + x;
}
void stuff::operator +(string s) //运算符重载的实装
{
name = name + s;
}
int main ( ).
{
stuff st2("C语言中文网", 3);
st2 + 2; //+运算
st2 + ".C++ 60分钟入门教程"; //+运算
cout << st2.getName() << st2.getAge() << endl;
return 0;
}

运行结果:
C语言中文网—3
C语言中文网.C++ 60分钟入门教程5

拷贝构造函数和赋值运算符

“拷贝构造函数”和“赋值运算符”都是将对象的值复制一份然后传给另一对象。这二个功能也是类本身就具有的,但有很多场合原封不动地复制给另外一个对象时反而会出错,例如在成员函数中有动态分配内存,或者参数指针指向外部某一地址时,就有可能出错。

要避免这些错误,我们可以重载“=”运算符以及拷贝构造函数,将出错的因素排除。下例中为了演示,故意将赋值运算符重载函数中不复制“姓名”,而拷贝构造函数中固定“年龄”。

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
35
36
37
38
39
#include <iostream>
#include <string>
using namespace std;
class stuff {
string name;
int age;
public:
stuff(string n, int a):name(n),age(a)
{
cout << "构造函数 " << name << age << endl;
}
string getName() {
return name;
}
int getAge() {
return age;
}
stuff& operator =(stuff& x); //赋值运算符重载
stuff(stuff& x):name(x.name),age(20) //拷贝构造函数重载
{
cout << "拷贝构造函数 " << name << age << endl;
}
};
stuff& stuff::operator =(stuff& x)
{
age = x.age;
cout << "赋值运算符 " << name << age << endl;
return *this;
}
int main ( )
{ stuff st("C语言中文网", 3); //调用通常的构造函数
stuff st1("C++ 60入门教程", 2); //调用通常的构造函数
st1 = st; //因为不产生新的实例,所以调用的是赋值运算符
stuff st2 = st; //因为产生新的实例,所以调用的是拷贝构造函数
cout << st.getName() << st.getAge() << endl;
cout << st1.getName() st1.getAge() << dl;
cout << st2.getName() << st2.getAge() << endl;
return 0;
}

类型转换

当需要将当前类的实例直接赋值给其它类型的变量时自动转换类型,这其实还是运算符重载。当需要其它类型直接赋值给当前类的实例时,只要增加构造函数就行。

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 <iostream>
#include <string>
using namespace std;
class stuff
{
string name;
int age;
public:
stuff(string n, int a):name(n),age(a) { }
string getName() {
return name;
}
int getAge() {
return age;
}
operator int() { //stuff → int
return age;
}
operator string() { //stuff → string
return name;
}
};
int main ( )
{

stuff st("C语言中文网", 3);
string m_name = st; //stuff → string
int m_age = st; //stuff → int
cout << m_name << endl;
cout << m_age << endl;
return 0;
}

##C++继承

基类 派生类的构造函数

缺省构造函数的调用关系

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
#include <iostream>
#include <string>
using namespace std;
class CBase {
string name;
int age;
public:
CBase() {
cout << "BASE" << endl;
}
~CBase() {

cout << "~BASE" << endl;
}
};
class CDerive : public CBase {
public:
CDerive() {
cout << "DERIVE" << endl;
}
~CDerive() {
cout << "~DERIVE" << endl;
}
};

int main ( )
{
CDerive d;
return 0;
}

运行结果:
BASE
DERIVE
~DERIVE
BASE

有参数的传递:

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
nclude <iostream>
#include <string>
using namespace std;
class CBase {
string name;
public:
CBase(string s) : name(s) {
cout << "BASE: " << name << endl;
}
~CBase() {
cout << "~BASE" << endl;

}
};
class CDerive : public CBase {
int age;
public:
CDerive(string s, int a) : CBase(s), age(a) {
cout << "DERIVE: " << age << endl;
}
~CDerive() {
cout << "~DERIVE" << endl;
}
};

int main ( )
{
CDerive d("小雅", 27);
return 0;
}

派生类调用基类成员

多重继承调用
派生类中的同名函数

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
35
36
37
38
39
#ilude <iostream>
#include <string>
using namespace std;
class CBase {
string id;
string name;
public:
CBase(string s1, string s2) : name(s1), id("base-"+s2) { }
void show() {
cout << "name: " << name << endl;
cout << "id: " << id << endl;
}
};
class CDerive : public CBase {
string id;
int age;
public:
CDerive(string s1, int a, string s2) : age(a), id("DERIVE-"+s2), CBase(s1, s2) { }
void show() {
cout << "id: " << id << endl;
cout << "age: " << age << endl;
//派生类的内部调用基类的方法
CBase::show();

//show(); //如果误写成这样,将不断地重复循环地调用自身(即死循环)。
}
};
int main ( )
{
CDerive d("C语言中文网", 3, "8503026"); //派生类的实例
d.show(); //运行的是CDerive类的show()函数
cout << "-------------"<< endl;
//类的外部调用基类的方法
d.CBase::show(); //运行的是CBase类的show()函数
cout << "-------------"<< endl;
CBase b("C语言中文网", "8503026"); //基类当作普通类直接产生的实例
b.show(); //运行的是CBase类的show()函数
return 0;
}

运行结果:
id: DERIVE-8503026
age: 3
name: C语言中文网
id: base-8503026
//————-
name: C语言中文网
id: base-8503026
//————-
name: C语言中文网
id: base-8503026

多重继承的先后问题-菱形问题

虚函数和重虚函数

注意 虚继承和虚函数的区别;
抽象类不能实例化;

##多态的应用

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

#incde <iostream>
using namespace std;
class area {
double dim1, dim2;
public:
//设置数据
void setData(double d1, double d2) {
dim1 = d1, dim2 = d2;
}
//取数据
void getData(double& d1, double& d2) {

d1 = dim1, d2 = dim2;
}
//求面积
virtual double getArea() = 0; //纯虚函数
};
//长方形
class rectangle : public area {
public:
double getArea() {
double d1, d2;
getData(d1, d2);
return d1 * d2 ;

}
};
//三角形
class triangle : public area {
public:
double getArea() {
double d1, d2;
getData(d1, d2);
return d1 * d2 / 2 ;
}
};
int main ( )
{
area *p;
rectangle r; //长方形类的实例
triangle t; //三角形类的实例
r.setData(35, 44);
t.setData(36, 25);
p = &r;
cout << "长方形的面积为: " << p->getArea() << endl;
p = &t;
cout << "三角形的面积为: " << p->getArea() << endl;
return 0;
}

队列和栈实现:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#inclu <iostream>
#include <cstdlib>
#include <cctype>
using namespace std;
class list {
public:
list *head; //头指针
list *tail; //尾指针
list *next; //下一个指针
int num; //当前节点的值
list() { head = tail = next = NULL; } //构造函数

virtual void store(int i) = 0; //增加节点
virtual int retrive() = 0; //删除节点
};
//队列
class queue : public list {
public:
//增加节点
virtual void store(int i) {
list *item = new queue;
if (!item) {
cout << "内存分配失败!" << endl;
exit(1);

}
item->num = i;
//链表末尾插入
if (tail) tail->next = item;
tail = item;
item->next = NULL;
if (!head) head = tail;
}
//取值并删除节点
int retrive() {
int i;
list *p;
if (!head) {
cout << "链表已经为空" << endl;
return 0;
}
//从链表头部删除
i = head->num;
p = head;
head = head->next;
delete p;
return i;
}
};
//堆栈
class stack : public list {
public:
//增加节点
virtual void store(int i) {
list *item = new stack;
if (!item) {
cout << "内存分配失败!" << endl;
exit(1);
}
item->num = i;
//链表头部插入
if (head) item->next = head;
head = item;
if (!tail) tail = head;
}
//取值并删除节点
int retrive() {
int i;
list *p;
if (!head) {
cout << "链表已经为空" << endl;
return 0;
}
//从链表头部删除
i = head->num;
p = head;
head = head->next;
delete p;
return i;
}
};
int main ( )
{
list *p;
//队列的演示
queue q;
p = &q;
p->store(111), p->store(222), p->store(333);
cout << "队列: ";
cout << p->retrive() << " ";
cout << p->retrive() << " ";
cout << p->retrive() << endl;
//堆栈的演示
stack s;
p = &s;
p->store(111), p->store(222), p->store(333);
cout << "堆栈: ";
cout << p->retrive() << " ";
cout << p->retrive() << " ";
cout << p->retrive() << endl;
return 0;
}

C++实例指针

参与成员变量重名;
判断对象是否为自己;
运算符重载

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
35
#includeiostream>
#include <string>
using namespace std;
class rect {
int x1, y1, x2, y2; //矩形座标
public:
rect() {
x1 = 0, y1 = 0, x2 = 0, y2 = 0;
}
rect(int m1, int n1, int m2, int n2) {
x1 = m1, y1 = n1, x2 = m2, y2 = n2;

}
void print() {
cout << " x1=" << x1;
cout << " y1=" << y1;
cout << " x2=" << x2;
cout << " y2=" << y2;
cout << endl;
}
rect operator++() {
x1++, y1++, x2++, y2++;
return *this; //返回当前实例
}

};
int main ( )
{
rect r(12, 20, 50, 40);
r.print();
rect obj;
obj = r++;
obj.print();
return 0;
}

编译有问题 通不过;

C++构造函数、explicit、类合成

默认转换带来一定的方便,同时也造成不必要的失误,为了防止失误,可以在构造函数前加“explicit”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <cstdlib>
using namespace std;
class classA {
int x;
public:
explicit classA(int x) { this->x = x; }
explicit classA(char *x) { this->x = atoi(x); }
int getX() { return x; }
};
int main ( )

{
classA ca(5); //正常调用构造函数
cout << "x = " << ca.getX() << endl;
ca = 100; //编译出错
cout << "x = " << ca.getX() << endl;
ca = "255"; //编译出错
cout << "x = " << ca.getX() << endl;
return 0;
}

类的合成

当一个类的成员变量的类型是另一个类时,称之为“合成(composite)”。构造函数在对这个变量赋值时,不能在函数体里面赋值,只能用“第6节”中的方法2,即创建时赋值。

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 classB {
int x;
public:
classB(int x) { this->x = x; }
int getB() { return x; }
};
class classA {
classB xb;
public:

//classA(classB b) { xb = b; } //编译出错
classA(classB b) : xb(b) { } //只能用这种方法
int setX() { return xb.getB(); }
};
int main ( )
{
classB cb(5); //先定义一个classB的实例
classA ca(cb); //然后用这个实例创建classA的实例
cout << "x = " << ca.getX() << endl;
return 0;
}

new delete

delete[]iArr;//注意数组的删除方法

静态

C语言中也用static来修饰变量,C++中除了修饰变量外还可以修饰类的成员:

修饰函数中的局部变量,这个变量在这个函数中,无论调用多少次都是共有的。

修饰全局变量,则这个变量在当前文件中有效。在另一文件中,即使是同一程序也不再有效。

修饰类的成员变量或成员函数,则这个类无论产生多少实例,这个成员都是共有的。

static修饰的变量的初始值为0。

在类的静态成员函数中不能使用this指针,也不能作成静态虚函数。

类的静态成员函数不能用const、volatile申明。
静态成员变量

下面演示静态成员变量的初始值、修改后的值和调用方法。

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
35
36
37
38
39
#include <iostream>
#include <string>
using namespace std;
class classA {
static int sx;
static string sstr;
public:
static int sy;
void set(int x, string s) {
sx = x;
sstr = s;

}
void print() {
cout << " x = " << sx ;
cout << " y = " << sy ;
cout << " s = " << sstr << endl;
}
};
//下面的申明是必须的,这样不会改变访问权限(public,private)
int classA::sx;
string classA::sstr;
int classA::sy;
int main ( )

{
classA ca1, ca2;
cout << "ca1: ", ca1.print(); //实例1的初始值
cout << "ca2: ", ca2.print(); //实例2的初始值
ca1.set(25, "C语言中文网"); //设定实例1,实例2 也会改变
ca1.sy = 100; //公有静态成员可以通过实例来设定
cout << "ca1: ", ca1.print(); //实例1的设定值
cout << "ca2: ", ca2.print(); //实例2的设定值
ca2.set(80, "C++ 60分钟入门教程"); //设定实例2,实例1 也会改变
classA::sy = 125; //公有静态成员也可以不通过实例来设定
cout << "ca1: ", ca1.print(); //实例1的设定值
cout << "ca2: ", ca2.print(); //实例2的设定值
return 0;
}

运行结果:
ca1: x = 0 y = 0 s =
ca2: x = 0 y = 0 s =
ca1: x = 25 y = 100 s = C语言中文网
ca2: x = 25 y = 100 s = C语言中文网
ca1: x = 80 y = 125 s = C++ 60分钟入门教程
ca2: x = 80 y = 125 s = C++ 60分钟入门教程
静态成员函数

笔者多年从事Java程序设计,按Java的经验,静态成员函数一般在公共程序中用的比较多。这样的程序与类自身关系不大,只是归类性质放在某个类中。还有一些与类转换或交换但不需要实例时使用的情况。
静态成员函数不能调用非晶态成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
class Integer {
public:
static int atoi(const char *s) {
return ::atoi(s);
}
static float atof(const char *s) {
return ::atof(s);
}
};

int main ( )
{
int x = Integer::atoi("322");
float y = Integer::atof("3.14");
cout << "x = " << x << " y = " << y << endl;
return 0;
}

运行结果:
x = 322 y = 3.14
类的静态常数

和静态成员变量类似功能,当不允许修改时,要定义成常数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
class Integer {
public:
static const int MAX_LEN = 100;
static const int MIN_LEN = 10;
};
//以下的申明不需要
//int Integer::MAX_LEN;
//int Integer::MIN_LEN;
int main ( )

{
cout << "MAX_LEN = " << Integer::MAX_LEN << endl;
cout << "MIN_LEN = " << Integer::MIN_LEN << endl;
return 0;
}

const

1 const修饰变量
2 const修饰类的成员函数
用const修饰类的成员函数,可以使函数内不能对任何成员变量修改。不是成员变量当然可以修改。
3 mutable与const相反
它修饰成员变量,即使在const成员函数中也能修改

const修饰指针

后续

C++基础
http://blog.csdn.net/WaitForFree/article/category/1434466

C++ WIN32编程

显式构造函数
http://blog.csdn.net/starlee/article/details/1331268
C++结构体
http://blog.sina.com.cn/s/blog_48f587a80100khtml
http://www.cnblogs.com/gnuhpc/archive/2012/12/10/2811html
struct Point
{
double x,y;
Point() {}
Point(double _x,double _y):x(_x),y(_y) {}
}
其中第一个函数起什么作用,一定要次次都写吗?
第二个函数这样写等价吗?
Point(double _x,double _y)

{
x=_x;
y=_y;
}

无参构造函数,需要都写,可能你用不到,但这是一种良好的习惯。1,2可合并为 Point(double _x=1,double _y=2):x(_x),y(_y) {}
等价。但当字符(字符串)赋值时,为了安全,不能用初始化列表形式(第一种)。
第一个是默认构造函数,这个函数什么都不做。这个构造函数有可能被编译器优化掉。
第二个构造函数的写法和你说的那个写法不一样。第一种写法中变量”初始化”的位置是在构造函数初始化列表,所以是真正的初始化。你说的那种写法不是初始化,是赋值。赋值和初始化是不一样的。
第一个构造函数不是一定要写的。。。。。类中有哪些构造函数要根据类的用途来看。

为什么在结构体里面自己写了个构造函数,被报错了,但是我加上默认形参值就不会报错了
struct EDGE
{
int from, to;
double cost;

EDGE(int a, int b, double c)//EDGE(int a = 0, int b = 0, double c = 0.0)
: from(a), to(b), cost(c){}

}edge[MAX MAX];
// 这里定义了一个 EDGE 类型的变量数组,由于没有提供参数,数组的每一个元素会调用默认构造函数初始化,而 EDGE 类没有提供默认构造函数,所以会报错。
数组里的对象,不论是类对象(struct或者class),还是基础数据类型,如果没有给初始化列表,一律执行值初始化,即基础数据类型都初始化为0,类对象执行默认构造函数(即不接受任何参数的构造函数)。
你写的struct里面因为自己定义了一个构造函数,所以编译器不会隐式地帮你声明一个默认构造函数,而你定义了一个数组,又没有默认构造函数,编译器就报错。
vector*
https://www.jianshu.com/p/d6963a62fdf2

runjs codepad

实例化的两种方式:一种指针。一种非指针。对应在堆中或者栈中

https://blog.csdn.net/sun980984305/article/details/52959893

https://blog.csdn.net/u010039733/article/details/51926966