编译器越来越软,规范性越来越弱

其实 C 和 C++ 有很多相通之处

# C 部分

# 动态空间

# malloc 分配空间

malloc分配空间
1
2
3
4
5
6
int main(){
int *p = malloc(10 * sizeof(int));
if (p == NULL) printf("Malloc Error!\n");
for (int i = 0;i < 10;i++) *(p + i) = i;
for (int i = 0;i < 10;i++) printf("%d", p[i]);
}

0123456789

# 高维数组

# 高维数组奇妙写法

高维数组奇妙写法
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(){
int a[3][3];
printf("%d\n", (a + 1)[2]);
printf("%d\n", *(a + 1 + 2));
// f[x] <=> *(f + x)
int (*p)[3][3] = &a;
int (*p1)[3] = a + 1;
int *p2 = a[0];

printf("%d\n", (*(a + 1))[2]);
printf("%d\n", *(*(a + 1) + 2));

}

6422020

6422020

0

0

# 高维数组初始化

高维数组初始化
1
2
3
4
5
6
7
8
9
10
11
int main(){
int arr[5] = {10, 20, 30, 40, 50};
int b = (arr+1)[2];
printf("is: %d\n",b);

int arr2[3][3] = {{10, 20, 30}, {40, 50, 60}, {70, 80, 90}};
int c = (arr2+1)[2];
printf("is: %d\n",c);
int d = (*(arr2+1))[2];
printf("is: %d\n",d);
}

is: 40

is: 6422004

is: 60

# 高维数组的传值

高维数组传值
1
2
3
4
5
6
7
8
9
10
11
void sort(int arr[]){
printf("%d\n", &arr);
arr[0] = 1000;
}
int main(){
int a[5] = {0, 1, 2, 3, 4};
printf("%d\n", a[0]);
printf("%d\n", &a);
sort(a);
printf("%d\n", a[0]);
}

0
6422016
6421984
1000

# 特别注意高维数组传值写法

特殊注意
1
2
3
4
5
6
7
8
9
10
11
12
13
void void1(int a[][3]){
}
void void2(int (*a)[3]){
}
/*
void void4(int **a){ // Wrong
}
*/
int main(){
int arr[3][3];
void1(arr);
void2(arr);
}

# 共用体

共用体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef union Node{
int a;
char b;
} Node;
typedef struct Node1{
int a;
} Node1;

int main(){
Node a;
a.a = 3;
printf("%d\n", a.a);
a.b = 'a';
printf("%d\n", a.a);
printf("%d\n", sizeof(a));
Node1 b;
}

3
97
4

由于共用体成员共享一段内存地址,所以改变其中一个的值其他值也会改变

# 函数指针

# 函数指针

demo1
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
int compare1(void const * A,  void const *B){
if (*(int *)A == *(int *)B) return 1;
return 0;
}

int compare2(void const * A, void const *B){
if (*(char *)A == *(char *)B) return 1;
return 0;
}

void search(void *a, void *b, int (*compare)(void const *A, void const *B)){
if (compare(a, b)) printf("yes!\n");
else printf("no!\n");
}

int (*func[])(void const *, void const *) = {compare1, compare2};

int main(int argc, char **argv){
int *p1, *p2;
char *c1, *c2;
p1 = (int*)malloc(4);p2 = (int*)malloc(4);
c1 = (char*) malloc(1); c2 = (char*) malloc(1);
*p1 = 1; *p2 = 2;
*c1 = 'a'; *c2 = 'a';
search(p1, p2, compare1);
search(c1, c2, compare2);
search(c1, c2, compare1);
printf("%d\n", func[0](p1, p2));
printf("%d\n", argc);
}

no!
yes!
yes!
0
1

根据我的理解 void 出现在函数参数里起到的泛型的作用,而 int *compare (…,…) 起到了传一个函数进去的作用

#

# 宏的特殊用法

demo1
1
2
3
4
5
6
7
8
9
10
11
12
13
#define Conn(x, y) x##y
#define ToChar(x) *#x
#define ToString(x) #x

int main(){
printf("%d\n", Conn(123, 456));
printf("%c\n", ToChar(5));
printf("%s\n", ToString(123456));
char *p = ToString(12345678912381098309213);
printf("%d\n", sizeof(p));
printf("%s\n", p);
return 0;
}

123456
5
123456
8
12345678912381098309213

注意 char * 的 size 恒为 8

# 柔性数组

demo1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _SoftStruct{
int len;
int array[];
} SoftStruct;

int main(){
SoftStruct a;
printf("%d\n", sizeof(a));
SoftStruct *p;
p = (SoftStruct*) malloc(sizeof(int) + sizeof(int) * (10));
p -> len = 10;
for (int i = 0;i < (p -> len);i++) (p -> array[i]) = i;
for (int i = 0;i < (p -> len);i++) printf("%d\n", p -> array[i]);
}

4
0
1
2
3
4
5
6
7
8
9

注意在结构体里最后一个成员变量 int array [] 并没有占空间,使用时才动态分配空间,且得到和其他成员变量连续的内存空间

# 字符串相关

# 初始化

demo1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(){
char* str = "Hello"; // 已经进行了内存分配,str = (char*) malloc(6 * sizeof(char))
char str1[5] = "Hell";
char str2[5] = {'H', 'e', 'l', 'l', '\0'}; // str1 <=> str2 等价
char str3[5] = "Hello";
printf("%s\n", str3); //注意str3最后少了个'\0',所以会继续打印连续地址村的str2
printf("%d %d %d\n", str3, str2, str1); //内存: H e l l o H e l l \0 H e l l \0
printf("%c\n", *(str + 1));
printf("str1 : %d %s\n", sizeof(str1), str1);
printf("str2 : %d %s\n", sizeof(str2), str2);
printf("%d\n", str1 == str2);
str2[2] = '\0';
printf("str2: %d %s\n", strlen(str2), str2);
printf("%c\n", *(str2 + 3));
int* intP = (int*) malloc(5 * sizeof(int));
intP[4] = 5; *(intP + 4) = 5;
return 0;
}

HelloHell
6422017 6422022 6422027
e
str1 : 5 Hell
str2 : 5 Hell
0
str2: 2 He
l

# strcpy 函数

demo2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(int argc,char* argv[])
{
char buf[2];
char *str = "hello world";
char (*q)[2] = &buf;
strcpy(buf,str);
printf("buf:%s\nsizeof(buf) = %ld\nstrlen(buf) = %ld\n",
buf,sizeof(buf),strlen(buf));
printf("%d %d %d\n", str, &str, buf);

char buf1[20];
strncpy(buf1,str,20);
printf("buf1:%s\nsizeof(buf1) = %ld\nstrlen(buf1) = %ld\n",
buf1,sizeof(buf1),strlen(buf1));
printf("%d %d %d\n", str, &str, buf);

return 0;
}

buf:hello world
sizeof(buf) = 2
strlen(buf) = 11
4210688 6422032 6422046
buf1:hello world
sizeof(buf1) = 20
strlen(buf1) = 11
4210688 6422032 6422046

注意 sizeof 是在编译时运算的分配的空间,而 strlen 是运行时遍历到第一个 \0,strcpy 原字符串长度过长时会引起覆盖掉 \0 导致不受控制的内存覆盖

# strcmp 函数

strcmp
1
2
3
4
5
6
7
8
9
10
11
int main(){
char a[] = "abc";
char b[] = "ab";
char c[] = "ad";
char d[] = "d";
printf("strcmp(\"abc\", \"ab\")=%d\n", strcmp(a, b));
printf("strcmp(\"ab\", \"abc\")=%d\n", strcmp(b, a));
printf("strcmp(\"ab\", \"ab\")=%d\n", strcmp(b, b));
printf("strcmp(\"ad\", \"abc\")=%d\n", strcmp(c, a));
return 0;
}

strcmp(“abc”, “ab”)=1
strcmp(“ab”, “abc”)=-1
strcmp(“ab”, “ab”)=0
strcmp(“ad”, “abc”)=1

就是字典序,大于输出 1,小于输出 - 1,等于输出 0.(为啥要记这东西

# 存储区域

char* 与 char[]
1
2
3
4
5
6
int main(){
char a[6] = "hello";
char *p = "123";
strcat(a, p); //没问题,a="hello123"
strcat(p, a); //错误
}

注意到,“123” 储存在常量区,而 char *p 在编译时就确定指向了它,不可被更改。而 char a [6] 字符数组在运行时才被初始化为”hello“,相当于在堆上 malloc 了一个空间。

# 指针初步

demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void swap(int* a, int* b)
{
int* temp = a;
a = b;
b = temp;
}
void swap1(int *a, int *b){
int t = *a;
*a = *b;
*b = t;
}
int main()
{
int a = 10;
int b = 20;
swap(&a,&b);
printf("a is %d, b is %d\n", a,b);
swap1(&a,&b);
printf("a is %d, b is %d\n", a,b);
return 0;
}

a is 10, b is 20
a is 20, b is 10

demo2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void func(int a){
printf("%x\n", &a);
}
int main(){
int a = 10;
int* p;
p = &a;
int *p1;
*p1 = 10;
printf("%x %x\n", p1, *p1);

printf("p is %x\n", p);
printf("*p is %d\n", *p);

int* p2 = (int*)0x61fe04;
printf("*p2 is %d\n", *p2);

printf("%x ", &a);
func(a);
}

c013c0 a
p is 61fe04
*p is 10
*p2 is 10
61fe04 61fde0

# C++ 部分

# 运算优先级

demo1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct A{
int u;
};

int main(){
A a[10];
for (int i = 0;i < 10;i++) a[i].u = i + 1;

A* p = a;
cout << (++p)->u << endl;
cout << p - a << endl;
for (int i = 0;i < 10;i++) cout << a[i].u << " ";cout<<endl;

cout << ++p->u << endl;
cout << p - a << endl;
for (int i = 0;i < 10;i++) cout << a[i].u << " ";cout<<endl;
}

2
1
1 2 3 4 5 6 7 8 9 10
3
1
1 3 3 4 5 6 7 8 9 10

demo2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main(){
int a[10];
for (int i = 0;i < 10;i++) a[i] = 10 - i;
int *p = a;
cout << *p++ << endl; //<=> *(p++);
cout << p - a << endl;
for (int i = 0;i < 10;i++) cout << a[i] << " ";cout<<endl;

for (int i = 0;i < 10;i++) a[i] = 10 - i;
p = a;
cout << (*p)++ << endl;
cout << p - a << endl;
for (int i = 0;i < 10;i++) cout << a[i] << " ";cout<<endl;

p = a;
cout << *p + 2 << endl;
}

10
1
10 9 8 7 6 5 4 3 2 1
10
0
11 9 8 7 6 5 4 3 2 1
13

# (伪)局部函数

demo
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(){                 //重载()实现伪函数
struct{
void operator () (){
cout << "HELLO" << endl;
}
int operator() (int a, int b){
return a + b;
}
} foo;

foo();
cout << foo(1, 2);
}

HELLO
3

# 纯虚函数和抽象类

demo1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A{
public:
virtual void func() = 0; //只有virtual才能声明=0
};

class B : public A{ // A限制了B必须重写实现func()才可以被实例化
public:
void func(){
cout << "Hello";
}
};
int main(){
B b;
b.func();
return 0;
}

Hello

demo2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A{
public:
void func(){
cout << "Hello!";
}
};
class B : public A{
public:
void func()=delete; //这样禁止了所有的B的实例调用func()函数
};
class C{
public:
C()=delete; //特别地,把构造函数delete掉,则C永远无法实例化
};
int main(){
B b;
//b.func() 错误
//C c; 错误,无法实例化
return 0;
}

# 仿函数

demo
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
class Class_Func{   //虽然他是个类,但却起到函数的作用。
public:
bool operator ()(int num){
return num == 10 ? 1 : 0;
}
private:

};
bool func(int num){
return num == 10 ? 1 : 0;
}
void cal(int a[], int len, Class_Func f){
int res = 0;
For(i, 0, len) if (f(a[i])) res++;
printf("%d\n", res);
}
void cal(int a[], int len, bool (*func)(int num)){
int res = 0;
For(i, 0, len) if (func(a[i])) res++;
printf("%d\n", res);
}
int main(){
int a[5] = {10, 1, 10, 1,10};
Class_Func t;
cal(a, 5, t); //传了个类的对象进去,但是用处是作为一个函数
cal(a, 5, Class_Func()); //这么写也可以
cal(a, 5, func); //当然写函数指针也可以
return 0;
}

3
3
3

# 分配内存

# bad 版

stringbad.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class StringBad
{
private:
char * str; // pointer to string
int len; // length of string
static int num_strings; // number of objects
public:
StringBad(const char * s); // constructor
StringBad(); // default constructor
~StringBad(); // destructor
// friend function
friend std::ostream & operator<<(std::ostream & os,
const StringBad & st);
};
stringbad.cpp
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
using std::cout;

// initializing static class member
int StringBad::num_strings = 0;

// class methods

// construct StringBad from C string
StringBad::StringBad(const char * s)
{
len = std::strlen(s); // set size
str = new char[len + 1]; // allot storage
std::strcpy(str, s); // initialize pointer
num_strings++; // set object count
cout << num_strings << ": \"" << str
<< "\" object created\n"; // For Your Information
}

StringBad::StringBad() // default constructor
{
len = 4;
str = new char[4];
std::strcpy(str, "C++"); // default string
num_strings++;
cout << num_strings << ": \"" << str
<< "\" default object created\n"; // FYI
}

StringBad::~StringBad() // necessary destructor
{
cout << "\"" << str << "\" object deleted, "; // FYI
--num_strings; // required
cout << num_strings << " left\n"; // FYI
delete [] str; // required
}

std::ostream & operator<<(std::ostream & os, const StringBad & st)
{
os << st.str;
return os;
}
vegnew.cpp
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
// vegnews.cpp -- using new and delete with classes
// compile with strngbad.cpp
#include <iostream>
using std::cout;
#include "strngbad.h"

void callme1(StringBad &); // pass by reference
void callme2(StringBad); // pass by value

int main()
{
using std::endl;
{
cout << "Starting an inner block.\n";
StringBad headline1("Celery Stalks at Midnight");
StringBad headline2("Lettuce Prey");
StringBad sports("Spinach Leaves Bowl for Dollars");
cout << "headline1: " << headline1 << endl;
cout << "headline2: " << headline2 << endl;
cout << "sports: " << sports << endl; //到这里创建了三个StringBad对象
cout << endl;
callme1(headline1);
cout << "headline1: " << headline1 << endl; //这里输出headline1传引用未发生意外
callme2(headline2);
cout << "headline2: " << headline2 << endl; //这里将headline传值进入函数,而值出栈时会调用其析构函数
//但我在疑惑,明明传入时复制拷贝了一个对象,
//应该调用的是复制的对象的析构函数啊,为什么导致headline2空了呐
//发现赋值时,调用的是默认的*复制构造函数*,即BadString(const BadString &s)
//而其实现中有一句str = s.str;导致复制的对象和headline2的str指向同一块地址!
//所以复制的对象析构时直接把headline2的str也给delete了
cout << endl;
cout << "Initialize one object to another:\n";
StringBad sailor = sports; //这里调用的也是默认的*复制构造函数*,即sailor=BadString(sports)
//这里计数器每增加是因为用*复制构造函数*创建的对象都不应该计数
//因为复制的对象str和原对象指向同一块地址!
cout << "sailor: " << sailor << endl;
cout << "Assign one object to another:\n";
StringBad knot;
knot = headline1;
cout << "knot: " << knot << endl; //这里会先调用默认无参构造函数,再赋值过去
cout << endl; //接下来是析构部分
cout << "Exiting the block.\n"; //这里会re,因为在headline2析构时,headline2的str早就被delete掉了
}
cout << "End of main()\n";
// std::cin.get();
return 0;
}

void callme1(StringBad & rsb)
{
cout << "String passed by reference:\n";
cout << " \"" << rsb << "\"\n";
}

void callme2(StringBad sb)
{
cout << "String passed by value:\n";
cout << " \"" << sb << "\"\n";
}

Starting an inner block.
1: “Celery Stalks at Midnight” object created
2: “Lettuce Prey” object created
3: “Spinach Leaves Bowl for Dollars” object created
headline1: Celery Stalks at Midnight
headline2: Lettuce Prey
sports: Spinach Leaves Bowl for Dollars

String passed by reference:
“Celery Stalks at Midnight”
headline1: Celery Stalks at Midnight
String passed by value:
“Lettuce Prey”
“Lettuce Prey” object deleted, 2 left
headline2:

Initialize one object to another:
sailor: Spinach Leaves Bowl for Dollars
Assign one object to another:
3: “C++” default object created
knot: Celery Stalks at Midnight

Exiting the block.
“Celery Stalks at Midnight” object deleted, 2 left
“Spinach Leaves Bowl for Dollars” object deleted, 1 left
" 衑?

# good 版

  • good 版就是重写了 BadString (const BadString & s)、BadString & operator = (const BadString & s) 之类的一切可能产生复制对象的函数,把其中的 str = s.str 改成 str = new char [strlen (s.str) + 1], strcpy (str, s.str)。一定要手动新分配内存。

# 构造函数和析构函数

# 对象和指针的析构顺序

stock.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Stock{
private:
string company;
long shares;
double share_val;
double total_val;
void set_tot(){total_val = share_val * shares;}
public:
Stock();
Stock(const string &co);
virtual ~Stock();
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};
stock.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Stock::Stock(){
cout << "Stock default constructor is called!" << endl;
company = "no name";
}
Stock::Stock(const string &co):company(co){
cout << "Stock constructor with parameter is called!" << endl;
}
Stock::~Stock(){
cout << "Bye!" << " " << company << endl;
}
void Stock::show(){
cout << company << endl;
}
int main(){
Stock B = (string)"B"; //等价于 Stock B = Stock((string)"B");
Stock* A = new Stock();
delete A;
// return 0;
}

Stock constructor with parameter is called!
Stock default constructor is called!
Bye! no name
Bye! B

可以注意到默认的析构函数总是在出栈时调用(即总在手写实现 delete 后面

# 继承的构造与析构

demo.cpp
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
class BaseDemo
{
public:
BaseDemo() // Constructor
{
cout << "This is the BaseDemo constructor. \n";
}
~BaseDemo() // Destructor
{
cout << "This is the BaseDemo destructor.\n";
}
virtual void show(){
cout << "show from Base Demo!" << endl;
}
};

// Derived class
class DeriDemo : public BaseDemo
{
public:
DeriDemo() // Constructor
{
cout << "This is the DeriDemo constructor.\n";
}
~DeriDemo() // Destructor
{
cout << "This is the DeriDemo destructor.\n";
}
virtual void show(){
cout << "show from DeriDemo" << endl;
}
};
int main()
{
DeriDemo *A = new DeriDemo();
delete A;
cout << "\n";
BaseDemo *B = new DeriDemo();
B -> show();
return 0;
}

This is the BaseDemo constructor.
This is the DeriDemo constructor.
This is the DeriDemo destructor.
This is the BaseDemo destructor.

This is the BaseDemo constructor.
This is the DeriDemo constructor.
show from DeriDemo

可以注意到构造函数从基类开始,析构函数从派生类开始。此外,指针 new 的或者 malloc 分配的对象地址不手动删除时不会被自动 delete 掉。这里还可以注意下 B 虽然是基类指针却指向一个派生类,而虚函数 show 很好的体现了指针对象的类别。

而特别地,C++ 中将类的析构函数写成虚函数是很好的习惯。防止在多态时用基类指针指向派生类时,析构却没有调用派生类析构函数导致内存泄漏。

进一步地,为什么要用基类指针指向派生类呢?这就是继承与多态的内容了。可以理解多态为 “接受来自基类的同一指令,但却根据不同的派生类产生不同的行为”。譬如基类是 “图形”,而它的派生类有 “矩形”“三角形”“圆” 等等。而图形都可以求面积,所以 getArea () 函数应该写在基类里,并且写成虚函数,根据不同的派生类进行不同的面积公式计算。而 “接受同一指令” 即需要使用基类指针。即我不关心你究竟是三角形还是矩形,我只关心其可以求面积。事实上就是把接口与实现分离.{dot},基类的虚函数说明了对外界能使用的函数信息,不同的派生类进行自己独特的实现。

# 多继承

djc.cpp
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
class A{
public:
A(){
printf("A()\n");
}
virtual ~A(){
printf("~A()\n");
}
};

class B : public A{
public:
B(){
printf("B()\n");
}
virtual ~B(){
printf("~B()\n");
}
};

class C{
public:
C(){
printf("C()\n");
}
virtual ~C(){
printf("~C()\n");
}
};

class D : public C, public B{
public:
D(){
printf("D()\n");
}
virtual ~D(){
printf("~D()\n");
}
};

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

C()
A()
B()
D()
~D()
~B()
~A()
~C()

  • D 继承自 C,B,B 继承自 A。同级间写在前面的先构造,即要构造 D,先构造 C,然后构造 B,要构造 B,先构造 A,于是构造顺序为 C-A-B-D

# 局部类

jbl.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
class c4
{
public:
int a;
void foo() {a = 4;}
};

class c4 ff;
ff.foo();
cout << ff.a << endl;
return 0;
}
  • 这个用处属实少

# 类继承

这部分不想给 demo 了,主要是注意几个点:

demo1
1
2
3
4
5
6
//A是一个类
A a; //等价于调用默认构造函数 A a = A(); A()
A a(); //不会创建对象,等价于定义了一个返回值是A的函数,没有函数体
A a = b; //等价于调用默认复制构造函数 A a = A(b); A(const A &a)
A a;
a = b; //等价于先调用默认构造函数A a = A();然后调用A.operator = (const A &a)复制··
demo2
1
2
3
4
5
6
7
8
void func(A a){
}
int main(){
A a;
func(a); //实际上,传值进去时,会先调用复制构造函数A(const A &a)复制一个新的对象
//函数结束后,会调用复制的对象的析构函数
//如果不手写A(const A& a),很有可能导致a中指针类型成员变量和复制出来的对象指向同一块 地址,在析构时被delete掉。详见分配内存bad版
}
demo3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person{
public:
Person(int _id, char* _name);
~Person();
Person();
Person(const Person& pr)=delete;
public:
Person& operator=(const Person& pr)=delete; //删除一定的构造方法和运算符可以限制类的使用
//譬如这个类就无法被复制,也无法被赋值,可以避免被函数传值

private:
int id;
char* name;
private:
static int counter;
};

# 枚举类

demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Weekdays{
MON, TUE, WED, THU = 6, FRI, SAT, SUN
}; //类似于#define MON 0 #define TUE 1...但是方便写和维护
//不写默认升序。这个例子中MON=0,TUE=1,WED=2,THU=6,FRI=7,SAT=8,SUN=9
//实际上就是把抽象的编码信息变成可读的变量名。例如1你不知道他在说什么1,但是MON就知道是指星期一
int main(){
Weekdays rua = WED;
switch(rua){
case MON:
break;
case TUE:
break;
}
return 0;
}

# 命名空间

demo
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
namespace myspace1
{
extern int gvar;//内部声明
using std::cout;
using std::endl;

class myclass
{
public:
void print()
{
cout<<"in space1,gvar="<<gvar<<endl;
}
};
}


namespace myspace2
{
using std::cout;
using std::endl;
int i=5;

class myclass
{
public:
void print(){
cout<<"in space2"<<endl;
}
};
namespace nestedspace
{
void ExternFunc();//内部声明
}
}

//外部定义
int myspace1::gvar=1;
void myspace2::nestedspace::ExternFunc()
{
cout<<"in nestedspace"<<endl;
}

int main(int argc,char* argv[])
{
myspace1::myclass obj1;
obj1.print();
myspace2::myclass obj2;
obj2.print();
myspace2::nestedspace::ExternFunc();
}

in space1,gvar=1
in space2
in nestedspace

注意” 外部定义 “+” 内部声明 “和” 内部声明 “+” 外部实现 “的操作,虽然我也不知道为啥,明明内部定义 + 内部实现挺不错啊据说老版本会报错

匿名命名空间
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
using namespace std;

namespace
{
double dvar=1.8;
}
void show1()
{
cout<<"dvar:"<<dvar<<endl;
}

int main(int argc,char* argv[])
{
void show2();
show1();
show2();
}

//a.cpp
#include <iostream>
using namespace std;

double dvar=2.8;
void show2()
{
cout<<"dvar:"<<dvar<<endl;
}

1.8

2.8

匿名命名空间可以将 struct 和 class 的对象前都加上”static“修饰,如果定义了相同的变量名也不会报错。匿名命名空间中的变量仅在包含该匿名命名空间的源文件中可见。

# 模板

模板函数
1
2
3
4
5
6
7
8
9
10
template<typename T> int compare(const T &a, const T &b){
return a == b ? 0 : (a > b ? 1 : -1);
}

int main(){
cout << compare(1, 2) << endl;
cout << compare("a", "b") << endl;
cout << compare('a', 'b') << endl;
return 0;
}

-1
1
-1

模板类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <class T> class Queue {
public:
Queue (); // default constructor
T &front (); // return element from head of Queue
const T &front () const;
void push (const T &); // add element to back of Queue
void pop(); // remove element from head of Queue
bool empty() const; // true if no elements in the Queue
private:
// ...
};
int main(){
Queue<int> Q;
Queue<char> Q1;
return 0;
}

# 嵌套类

demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class c1{
public:
int a;
void foo();
class c2{
public:
int a;
void foo();
} b;
};
void c1::foo(){
a = 1;
}
void c1::c2::foo(){
a = 2;
}
int main(){
class c1 f;
f.foo();
f.b.foo();
cout << f.a << endl;
cout << f.b.a << endl;
return 0;
}

1
2

# 虚函数

demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A{
public:
virtual void print(){cout<<"This is A"<<endl;}
};
class B : public A{
public:
void print(){cout<<"This is B"<<endl;}
};

int main(){
A a;
B b;
A *p1 = &a;
A *p2 = &b;
p1->print();
p2->print();
return 0;
}

This is A
This is A

demo1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A{
public:
virtual void print(){cout<<"This is A"<<endl;}
};
class B : public A{
public:
virtual void print(){cout<<"This is B"<<endl;}
};
int main(){
A a;
B b;
A *p1 = &a;
A *p2 = &b;
p1->print();
p2->print();
return 0;
}

This is A
This is B

# 异常

char*异常
1
2
3
4
5
6
7
8
9
10
11
12
void divide(int a, int b){
if (b == 0) throw "rua!";
a /= b;
}
int main(){
try{
divide(1, 0);
}catch(const char* e){
cout << e << endl;
}
return 0;
}

rua!

自定义异常
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
class myexception : public std::exception {
public :
myexception(std::string s) : message(s){

}
virtual const char *what(){
return message.data();
}
private:
std::string message;
};

void Func() {
try{
int *p = NULL;
if (p == NULL) {
throw myexception("p is NULL");
}
}
catch(myexception e) {
printf("%s\n", e.what());
throw; //重新抛出异常,异常类型为myexception
}catch (std::exception e) {
printf("%s", e.what());
}
}
int main(){
try{
Func();
} catch(myexception e){
printf("%s\n", e.what());
} catch(...){

}
return 0;
}

p is NULL
p is NULL

demo
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
class Myexception : public std::exception {
public :
Myexception(std::string s){
message = new char[s.length() + 1];
for (int i = 0;i < s.length();i++) message[i] = s[i];
}
Myexception(){ message = NULL; }
virtual const char *what(){
if (message == NULL) throw "unknown exception!";
return message;
}
virtual ~Myexception(){
delete message;
}
private:
//std::string message;
char *message;
};

void foo(){
char *p = NULL;
if (p == NULL) throw Myexception();
}

int main(){
try{
foo();
} catch(Myexception e){
try{
printf("%s\n", e.what());
}catch(const char* s){
printf("%s\n", s);
}
} catch(...){
}
return 0;
}

unknown exception!

我验证了 virtual const char *what () 里面也可以抛异常

# 引用

demo
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
int val[10];
int &put(int idx){
return val[idx];
}
void swap(int &a, int &b){
a ^= b ^= a ^= b;
}
int res;
int fn1(int r){
res = r * r;
return res;
}
int& fn2(int r){
res = r * r;
return res;
}
int main(){
put(0) = 1;
put(1) = 2;
For(i, 0, 2) printf("%d\n", val[i]);

int a = 1, b = 2;
int &ra = a, &rb = b;
swap(ra, rb);
printf("%d %d\n", a, b);

int r = 3;
int x = fn1(r), y = fn2(r);
int &ry = fn2(r);// &rx = fn1(r);不能这么写
cout << x << " " << y << " " << /*rx <<*/ " " << ry << endl;
return 0;
}

1
2
2 1
9 9 9

demo2
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
struct struct_A{
int a[10];
/*
int operator[] (int idx){
return a[idx]; //如果返回值是int不是int&的话,a[3]=10;将非法
}*/
/*
int* operator[] (int idx){
return a + idx; //如果返回值是int*的话,赋值语句就要改成*a[3]=10;非常麻烦
}*/
int& operator[](int idx){
return a[idx];
}
};
void func1(int a){} //这样写传入参数时会拷贝一份a,效率低下
void func2(int *a){} //这样写传入参数指针有可能被修改,比较危险
void func3(int& a){} //这样写没有拷贝,但传入的变量可以被修改
void func4(const int& a){} //这样写既没有拷贝,也不可被修改

int main(){
struct_A a;
a[3] = 10;
cout << a.operator[](3) << endl;

int b = 10;
int& rb = b;
printf("%d %x %x\n", rb, &b, &rb);
return 0;
}

10
10 61fdec 61fdec

# 友元

demo1
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
class A{
friend class B; //友元类
friend void print(const A &a); //友元函数 友元就是向外部的类和函数公开private
friend int main(); //草,我写着玩的
private:
int value = 0;
public:
};

void print(const A &a){
printf("%d from A!\n", a.value);
}

class B{
private:
A a;
public:
void set(){
a.value = -100;
}
void print(){
printf("%d\n", a.value);
}
};

int main(){
B b;
b.print();
b.set();
b.print();

A a;
print(a);
return 0;
}

0
-100
0 from A!

# 重载运算符

demo1
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
class Time{
friend Time operator += (Time& A, const Time& B);
friend ostream & operator << (ostream &os, const Time& A);
private:
int minute, hour;
public:
Time operator + (const Time& B){ //直接写在类里面
Time res;
res.hour = this -> hour + B.hour + (this -> minute + B.minute) / 60;
res.minute = (this -> minute + B.minute) % 60;
return res;
}

friend ostream & operator << (ostream &os, const Time& A){ //重载 << 写成友元在外部直接调用
os << "(" << A.hour << ":" << A.minute << ")";
return os;
}
/*
Time operator << (const ostream &os){
os << "(" << hour << ":" << minute << ")";
return *this;
} //这样写的话主程序调用就成了a << cout << cout << cout;无法同时输出a,b
*/
ostream & operator << (ostream &os){
os << "(" << hour << ":" << minute << ")";
return os;
} //这样写的话就成了a << cout << b; 需要配合上未注释掉的friend版本
//总之,返回类型要是ostream,而且<<右侧元素是A最好的写法就是友元


void set(int hour, int minute){
this -> hour = hour;
this -> minute = minute;
}
void print(){
printf("%d:%d\n", hour, minute);
}
};

Time operator += (Time& A, const Time& B){ //利用友元写在类外面
A = A + B;
return A;
}
int main(){
Time a, b;
a.set(1, 30);
b.set(3, 40);
a = a + b;
a.print();
a += b;
a.print();
cout << a << b << endl;
a << cout << b << endl;
return 0;
}

5:10
8:50
(8:50)(3:40)
(8:50)(3:40)

我发现在重载运算符时,写不写 friend 的一个直接影响是 operator 函数参数的个数。如果写了 friend 就能填两个,否则只能填一个

demo2
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
class A{
private:
int a[10];
void set(){
for (int i = 0;i < 10;i++) a[i] = -i;
}
public:
int & operator [] (int idx){
return a[idx];
}
void operator () (){
set();
return;
}
A & operator << (ostream &os){
os << a[3];
return *this;
} //这就是demo1里面被注释掉的版本
};
int main(){
A a;
a[2] = 114514;
a();
printf("%d %d\n", a[2], a[3]);
(a << cout) << cout;
return 0;
}

-2 -3
-3-3

# new 和 delete

demo1
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
class T{
public:
T(){
cout << "T()" << endl;
w = 0;
}

~T(){
cout << "~T()" << endl;
}
int w;
};

int main(){
T t = T();
t.w = 10;

T* p = new(&t) T; //new(地址) T,在指定地址往后sizeof(T)长度的内存上新建个T
cout << t.w << endl;

char a[sizeof(T)];
p = new(a) T; //new(地址) T,在指定地址往后sizeof(T)长度的内存上新建个T
//要注意,编译阶段就会对new()里面的地址是否留足了sizeof(T)进行检查。
//若写成 char a[sizeof(T) - 1]编译阶段便会warning

p = new(a) T; //operator new
p = new T(); //new operator
return 0;
}

T()
T()
0
T()
T()
T()
~T()

# const 关键字

指针
1
2
3
4
5
6
7
8
9
10
11
int main(){
char s[] = "abc";

char *const p1 = s;
cout << *p1 << endl; //只能输出a,因为p1是个const,无法p1++输出后一位
char const *p2 = s;
//*p2 = "d"; //无法修改p2指向的值,因为p2指向的是个char const不可修改是只读
//报错:error: assignment of read-only location '* p2'
const char *p3 = s; //等价于p2,从右往左读指向了个const char类型无法修改
return 0;
}

a

demo2
1
2
3
4
5
6
7
8
9
10
11
12
13
void func1(int *const a){
//a++; 同demo1,这样写是错的
}

void func2(int const *a){
//*a = 4; 无法修改,报错: error: assignment of read-only location '* a'
}
int main(){
int *a = new int(3);
func1(a);
func2(a);
return 0;
}
demo3
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
int d = 0;
class A{
public:
A(){}
void func1(){
//a = 10;
b = 10;
}
const void func2(){
//a = 10; a是const 不可改
b = 10; //但const成员函数可以改成员变量!和网上大多说的不同
//感觉和普通成员函数没区别了
}
void func3() const{ //const写在这个位置只出现于成员函数,使得函数内部不得修改成员变量
//b = 10; 错误,常函数不得修改成员变量
d = 10; //但全局变量可以
}
const int a = 1;
int b = 2;
};

int main(){
const A a;
A b;
//a.b = 3; 错误,a是const的全部成员变量都不可动
//a.func1(); a.func2(); 错误,a是const的a无法访问其成员函数
cout << a.a << " " << a.b << endl;

b.func2();
cout << b.a << " " << b.b << endl;
}

1 2
1 10

Edited on Views times