zzm99


  • Home

  • Tags

  • Categories

  • Archives

  • Search

excel基础知识1

Posted on 2019-07-18 | In excel |
Words count in article: 730 | Reading time ≈ 2

excel基础知识

1. 利用office助手进行查询:告诉我您想要做什么…

2. 数据输入:

  • 单元格内换行:Alt+Enter

  • 电话、身份证类的文本型数据输入时,先输入英文单引号(以文本形式保存数据)(否则将以数字类型保存电话号码等,可能会出现省略现象例如:123123123123变成1.23123E+11)

  • 单元格列宽小于录入数据时,多余的数据会在相邻单元格显示,如果邻格也有数据,就会被截断不显示,但数据仍然存在。

  • 快速填充相同数据:选中对应单元格,移到右下角显示+,拖动。

  • 快速填充有序数据:输入前两个数据;选中前两个单元格;右下角拖动

  • 同时快速填充可以同时拖动多列多行

  • 修改数据:直接修改;右键清楚内容;键盘delete键

  • 移动复制:移动对应单元格:ctrl+X–ctrl+V;复制对应单元格:ctrl+C–ctrl+V

  • 查找替换数据:ctrl+F,选择对应功能

Read more »

C++ 多线程

Posted on 2019-07-06 | In C++ , 其他 |
Words count in article: 2.1k | Reading time ≈ 9

Linux

C++ 多线程

多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。

  • 基于进程的多任务处理是程序的并发执行。
  • 基于线程的多任务处理是同一程序的片段的并发执行。

多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。

创建线程

下面的程序,我们可以用它来创建一个 POSIX 线程:

1
2
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)

在这里,pthread_create 创建一个新的线程,并让它可执行。下面是关于参数的说明:

参数 描述
thread 指向线程标识符指针。
attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

终止线程

使用下面的程序,我们可以用它来终止一个 POSIX 线程:

1
2
#include <pthread.h>
pthread_exit (status)

在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

以下简单的实例代码使用 pthread_create() 函数创建了 5 个线程,每个线程输出”Hello Runoob!”:

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 <iostream>
// 必须的头文件
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

// 线程的运行函数
void* say_hello(void* args)
{
cout << "Hello Runoob!" << endl;
return 0;
}

int main()
{
// 定义线程的 id 变量,多个变量使用数组
pthread_t tids[NUM_THREADS];
for(int i = 0; i < NUM_THREADS; ++i)
{
//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
if (ret != 0)
{
cout << "pthread_create error: error_code=" << ret << endl;
}
}
//等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
pthread_exit(NULL);
}
1
2
3
4
5
6
7
8
9
10
11
使用 -lpthread 库编译下面的程序:

$ g++ test.cpp -lpthread -o test.o
现在,执行程序,将产生下列结果:

$ ./test.o
Hello Runoob!
Hello Runoob!
Hello Runoob!
Hello Runoob!
Hello Runoob!

以下简单的实例代码使用 pthread_create() 函数创建了 5 个线程,并接收传入的参数。每个线程打印一个 “Hello Runoob!” 消息,并输出接收的参数,然后调用 pthread_exit() 终止线程。

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
//文件名:test.cpp

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

void *PrintHello(void *threadid)
{
// 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
int tid = *((int*)threadid);
cout << "Hello Runoob! 线程 ID, " << tid << endl;
pthread_exit(NULL);
}

int main ()
{
pthread_t threads[NUM_THREADS];
int indexes[NUM_THREADS];// 用数组来保存i的值
int rc;
int i;
for( i=0; i < NUM_THREADS; i++ ){
cout << "main() : 创建线程, " << i << endl;
indexes[i] = i; //先保存i的值
// 传入的时候必须强制转换为void* 类型,即无类型指针
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&(indexes[i]));
if (rc){
cout << "Error:无法创建线程," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
现在编译并执行程序,将产生下列结果:

$ g++ test.cpp -lpthread -o test.o
$ ./test.o
main() : 创建线程, 0
main() : 创建线程, 1
Hello Runoob! 线程 ID, 0
main() : 创建线程, Hello Runoob! 线程 ID, 21

main() : 创建线程, 3
Hello Runoob! 线程 ID, 2
main() : 创建线程, 4
Hello Runoob! 线程 ID, 3
Hello Runoob! 线程 ID, 4

向线程传递参数

这个实例演示了如何通过结构传递多个参数。您可以在线程回调中传递任意的数据类型,因为它指向 void,如下面的实例所示:

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
#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

struct thread_data{
int thread_id;
char *message;
};

void *PrintHello(void *threadarg)
{
struct thread_data *my_data;

my_data = (struct thread_data *) threadarg;

cout << "Thread ID : " << my_data->thread_id ;
cout << " Message : " << my_data->message << endl;

pthread_exit(NULL);
}

int main ()
{
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int rc;
int i;

for( i=0; i < NUM_THREADS; i++ ){
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = (char*)"This is message";
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&td[i]);
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
当上面的代码被编译和执行时,它会产生下列结果:

$ g++ -Wno-write-strings test.cpp -lpthread -o test.o
$ ./test.o
main() : creating thread, 0
main() : creating thread, 1
Thread ID : 0 Message : This is message
main() : creating thread, Thread ID : 21
Message : This is message
main() : creating thread, 3
Thread ID : 2 Message : This is message
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 4 Message : This is message

连接和分离线程

我们可以使用以下两个函数来连接或分离线程:

1
2
pthread_join (threadid, status) 
pthread_detach (threadid)

pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。

这个实例演示了如何使用 pthread_join() 函数来等待线程的完成。

实例

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
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>

using namespace std;

#define NUM_THREADS 5

void *wait(void *t)
{
int i;
long tid;

tid = (long)t;

sleep(1);
cout << "Sleeping in thread " << endl;
cout << "Thread with id : " << tid << " ...exiting " << endl;
pthread_exit(NULL);
}

int main ()
{
int rc;
int i;
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;

// 初始化并设置线程为可连接的(joinable)
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

for( i=0; i < NUM_THREADS; i++ ){
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL, wait, (void *)&i );
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}

// 删除属性,并等待其他线程
pthread_attr_destroy(&attr);
for( i=0; i < NUM_THREADS; i++ ){
rc = pthread_join(threads[i], &status);
if (rc){
cout << "Error:unable to join," << rc << endl;
exit(-1);
}
cout << "Main: completed thread id :" << i ;
cout << " exiting with status :" << status << endl;
}

cout << "Main: program exiting." << endl;
pthread_exit(NULL);
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread
Thread with id : 4 ...exiting
Sleeping in thread
Thread with id : 3 ...exiting
Sleeping in thread
Thread with id : 2 ...exiting
Sleeping in thread
Thread with id : 1 ...exiting
Sleeping in thread
Thread with id : 0 ...exiting
Main: completed thread id :0 exiting with status :0
Main: completed thread id :1 exiting with status :0
Main: completed thread id :2 exiting with status :0
Main: completed thread id :3 exiting with status :0
Main: completed thread id :4 exiting with status :0
Main: program exiting.

c++ 11 之后有了标准的线程库:

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
#include <iostream>

#include <thread>

std::thread::id main_thread_id = std::this_thread::get_id();

void hello()
{
std::cout << "Hello Concurrent World\n";
if (main_thread_id == std::this_thread::get_id())
std::cout << "This is the main thread.\n";
else
std::cout << "This is not the main thread.\n";
}

void pause_thread(int n) {
std::this_thread::sleep_for(std::chrono::seconds(n));
std::cout << "pause of " << n << " seconds ended\n";
}

int main() {
std::thread t(hello);
std::cout << t.hardware_concurrency() << std::endl;//可以并发执行多少个(不准确)
std::cout << "native_handle " << t.native_handle() << std::endl;//可以并发执行多少个(不准确)
t.join();
std::thread a(hello);
a.detach();
std::thread threads[5]; // 默认构造线程

std::cout << "Spawning 5 threads...\n";
for (int i = 0; i < 5; ++i)
threads[i] = std::thread(pause_thread, i + 1); // move-assign threads
std::cout << "Done spawning threads. Now waiting for them to join:\n";
for (auto &thread : threads)
thread.join();
std::cout << "All threads joined!\n";
}

C++ 信号处理

Posted on 2019-07-06 | In C++ , 其他 |
Words count in article: 839 | Reading time ≈ 3

C++ 信号处理

信号是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断。

有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件 \ 中。

信号 描述
SIGABRT 程序的异常终止,如调用 abort。
SIGFPE 错误的算术运算,比如除以零或导致溢出的操作。
SIGILL 检测非法指令。
SIGINT 接收到交互注意信号。
SIGSEGV 非法访问内存。
SIGTERM 发送到程序的终止请求。

signal() 函数

C++ 信号处理库提供了 signal 函数,用来捕获突发事件。以下是 signal() 函数的语法:

void (*signal (int sig, void (*func)(int)))(int);

这个函数接收两个参数:第一个参数是一个整数,代表了信号的编号;第二个参数是一个指向信号处理函数的指针。

让我们编写一个简单的 C++ 程序,使用 signal() 函数捕获 SIGINT 信号。不管您想在程序中捕获什么信号,您都必须使用 signal 函数来注册信号,并将其与信号处理程序相关联。看看下面的实例:

实例

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
#include <iostream>
#include <csignal>
#include <unistd.h>

using namespace std;

void signalHandler( int signum )
{
cout << "Interrupt signal (" << signum << ") received.\n";

// 清理并关闭
// 终止程序

exit(signum);

}

int main ()
{
// 注册信号 SIGINT 和信号处理程序
signal(SIGINT, signalHandler);

while(1){
cout << "Going to sleep...." << endl;
sleep(1);
}

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
Going to sleep....
Going to sleep....
Going to sleep....

现在,按 Ctrl+C 来中断程序,您会看到程序捕获信号,程序打印如下内容并退出:

1
2
3
4
Going to sleep....
Going to sleep....
Going to sleep....
Interrupt signal (2) received.

raise() 函数

您可以使用函数 raise() 生成信号,该函数带有一个整数信号编号作为参数,语法如下:

int raise (signal sig);

在这里,sig 是要发送的信号的编号,这些信号包括:SIGINT、SIGABRT、SIGFPE、SIGILL、SIGSEGV、SIGTERM、SIGHUP。以下是我们使用 raise() 函数内部生成信号的实例:

实例

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
#include <iostream>
#include <csignal>
#include <unistd.h>

using namespace std;

void signalHandler( int signum )
{
cout << "Interrupt signal (" << signum << ") received.\n";

// 清理并关闭
// 终止程序

exit(signum);

}

int main ()
{
int i = 0;
// 注册信号 SIGINT 和信号处理程序
signal(SIGINT, signalHandler);

while(++i){
cout << "Going to sleep...." << endl;
if( i == 3 ){
raise( SIGINT);
}
sleep(1);
}

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果,并会自动退出:

1
2
3
4
Going to sleep....
Going to sleep....
Going to sleep....
Interrupt signal (2) received.

Sleep 函数

功能:执行挂起一段时间,也就是等待一段时间在继续执行

用法:Sleep(时间)

注意:

  • (1)Sleep是区分大小写的,有的编译器是大写,有的是小写。
  • (2)Sleep括号里的时间,在windows下是已毫秒为单位,而LInux是已秒为单位。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
int a = 1;
while (a)
{
cout << "欢迎来到菜鸟教程!" << endl;
Sleep(100);
}
system("pause");
return 0;
}

C++ 预处理器

Posted on 2019-07-06 | In C++ , 其他 |
Words count in article: 1.7k | Reading time ≈ 7

C++ 预处理器

预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。

所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。

我们已经看到,之前所有的实例中都有 #include 指令。这个宏用于把头文件包含到源文件中。

C++ 还支持很多预处理指令,比如#include、#define、#if、#else、#line等

#define 预处理

#define 预处理指令用于创建符号常量。该符号常量通常称为宏,指令的一般形式是:

#define macro-name replacement-text

当这一行代码出现在一个文件中时,在该文件中后续出现的所有宏都将会在程序编译之前被替换为 replacement-text。例如:

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

#define PI 3.14159

int main ()
{

cout << "Value of PI :" << PI << endl;

return 0;
}

现在,让我们测试这段代码,看看预处理的结果。假设源代码文件已经存在,接下来使用 -E 选项进行编译,并把结果重定向到 test.p。现在,如果您查看 test.p 文件,将会看到它已经包含大量的信息,而且在文件底部的值被改为如下:

1
2
3
4
5
6
7
8
9
10
$ gcc -E test.cpp > test.p

...
int main ()
{

cout << "Value of PI :" << 3.14159 << endl;

return 0;
}

参数宏

您可以使用 #define 来定义一个带有参数的宏,如下所示:

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

#define MIN(a,b) (a<b ? a : b)

int main ()
{
int i, j;
i = 100;
j = 30;
cout <<"较小的值为:" << MIN(i, j) << endl;

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

较小的值为:30

条件编译

有几个指令可以用来有选择地对部分程序源代码进行编译。这个过程被称为条件编译。

条件预处理器的结构与 if 选择结构很像。请看下面这段预处理器的代码:

1
2
3
#ifdef NULL
#define NULL 0
#endif

您可以只在调试时进行编译,调试开关可以使用一个宏来实现,如下所示:

1
2
3
#ifdef DEBUG
cerr <<"Variable x = " << x << endl;
#endif

如果在指令 #ifdef DEBUG 之前已经定义了符号常量 DEBUG,则会对程序中的 cerr 语句进行编译。您可以使用 #if 0 语句注释掉程序的一部分,如下所示:

1
2
3
#if 0
不进行编译的代码
#endif

让我们尝试下面的实例:

实例

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 <iostream>
using namespace std;
#define DEBUG

#define MIN(a,b) (((a)<(b)) ? a : b)

int main ()
{
int i, j;
i = 100;
j = 30;
#ifdef DEBUG
cerr <<"Trace: Inside main function" << endl;
#endif

#if 0
/* 这是注释部分 */
cout << MKSTR(HELLO C++) << endl;
#endif

cout <<"The minimum is " << MIN(i, j) << endl;

#ifdef DEBUG
cerr <<"Trace: Coming out of main function" << endl;
#endif
return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
Trace: Inside main function
The minimum is 30
Trace: Coming out of main function

# 和 ## 运算符

# 和 ## 预处理运算符在 C++ 和 ANSI/ISO C 中都是可用的。

# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。

请看下面的宏定义:

实例

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using namespace std;

#define MKSTR( x ) #x

int main ()
{
cout << MKSTR(HELLO C++) << endl;

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

HELLO C++

让我们来看看它是如何工作的。不难理解,C++ 预处理器把下面这行:

cout << MKSTR(HELLO C++) << endl;

转换成了:

cout << "HELLO C++" << endl;

## 运算符用于连接两个令牌。下面是一个实例:

#define CONCAT( x, y ) x ## y

当 CONCAT 出现在程序中时,它的参数会被连接起来,并用来取代宏。例如,程序中 CONCAT(HELLO, C++) 会被替换为 “HELLO C++”,如下面实例所示。

实例

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using namespace std;

#define concat(a, b) a ## b
int main()
{
int xy = 100;

cout << concat(x, y);
return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

100

让我们来看看它是如何工作的。不难理解,C++ 预处理器把下面这行:

cout << concat(x, y);

转换成了:

cout << xy;

C++ 中的预定义宏

C++ 提供了下表所示的一些预定义宏:

宏 描述
LINE 这会在程序编译时包含当前行号。
FILE 这会在程序编译时包含当前文件名。
DATE 这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。
TIME 这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。 s

让我们看看上述这些宏的实例:

实例

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

int main ()
{
cout << "Value of __LINE__ : " << __LINE__ << endl;
cout << "Value of __FILE__ : " << __FILE__ << endl;
cout << "Value of __DATE__ : " << __DATE__ << endl;
cout << "Value of __TIME__ : " << __TIME__ << endl;

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
4
Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48

补充:

  1. #空指令,无任何效果
  2. #include包含一个源代码文件
  3. #define定义宏
  4. #undef取消已定义的宏
  5. #if如果给定条件为真,则编译下面代码
  6. #ifdef如果宏已经定义,则编译下面代码
  7. #ifndef如果宏没有定义,则编译下面代码
  8. #elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
  9. #endif结束一个#if……#else条件编译块
  10. #error停止编译并显示错误信息

文件中的#ifndef

头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。

还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:

1
2
3
4
#ifndef <标识> 
#define <标识>
......
#endif

<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h

1
2
3
4
#ifndef _STDIO_H_ 
#define _STDIO_H_
......
#endif

C++ 输入/输出运算符重载

Posted on 2019-07-05 | In C++ , 其他 |
Words count in article: 822 | Reading time ≈ 4

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
36
37
38
39
40
41
42
43
#include <iostream>
using namespace std;

class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
friend ostream &operator<<( ostream &output, const Distance &D )
{
output << "F : " << D.feet << " I : " << D.inches;
return output;
}

friend istream &operator>>( istream &input, Distance &D )
{
input >> D.feet >> D.inches;
return input;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11), D3;

cout << "Enter the value of object : " << endl;
cin >> D3;
cout << "First Distance : " << D1 << endl;
cout << "Second Distance :" << D2 << endl;
cout << "Third Distance :" << D3 << endl;


return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
4
5
6
7
$./a.out
Enter the value of object :
70
10
First Distance : F : 11 I : 10
Second Distance :F : 5 I : 11
Third Distance :F : 70 I : 10

习惯上人们是使用 cin>> 和 cout<< 的,得使用友元函数来重载运算符,如果使用成员函数来重载会出现 d1<<cout; 这种不自然的代码。

下面这个实例展示了如果运用成员函数来重载会出现的情况d1<<cout;

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
#include <iostream>
using namespace std;

class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
ostream& operator<<( ostream & os)
{
os<<"英寸:"<<feet<<"\n英尺:"<<inches;
return os;
}
};
int main ()
{
Distance d1(20,18);
d1<<cout;//相当于d1.operator<<(cout)
}

运行结果:

1
2
英寸:20
英尺:18

此外,如果不想写成友元函数,但还向使用在正常的编程顺序时候,应该把重载函数写在类外,即不把重载函数写为成员函数。

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
#include <iostream>
using namespace std;

class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
int& getfeet(){
return feet;
}
int& getinches(){
return inches;
}

};
ostream &operator<<( ostream &output, Distance &D )
{
output << "F : " << D.getfeet() << " I : " << D.getinches();
return output;
}

istream &operator>>( istream &input, Distance &D )
{
input >> D.getfeet() >> D.getinches();
return input;
}


int main()
{
Distance D1(11, 10), D2(5, 11), D3;

cout << "Enter the value of object : " << endl;
cin >> D3;
cout << "First Distance : " << D1 << endl;
cout << "Second Distance :" << D2 << endl;
cout << "Third Distance :" << D3 << endl;


return 0;
}

由于重载函数不是成员函数,所以无法直接通过“.”来访问private对象,需要添加public成员函数来做中介。

C++ 继承

Posted on 2019-07-05 | In C++ , 其他 |
Words count in article: 1.3k | Reading time ≈ 5

基类 & 派生类

一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:

class derived-class: access-specifier base-class
其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。

假设有一个基类 Shape,Rectangle 是它的派生类,如下所示:

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
#include <iostream>

using namespace std;

// 基类
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};

// 派生类
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};

int main(void)
{
Rectangle Rect;

Rect.setWidth(5);
Rect.setHeight(7);

// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;

return 0;
}
1
2
3
当上面的代码被编译和执行时,它会产生下列结果:

Total area: 35

访问控制和继承

派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。

我们可以根据访问权限总结出不同的访问类型,如下所示:

访问 public protected private
同一个类 yes yes yes
派生类 yes yes no
外部的类 yes no no

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。
  • 基类的重载运算符。
  • 基类的友元函数。

继承类型

当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。

我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

  • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
  • 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
  • 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

多继承

多继承即一个子类可以有多个父类,它继承了多个父类的特性。

C++ 类可以从多个类继承成员,语法如下:

1
2
3
4
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};

其中,访问修饰符继承方式是 public、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
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
#include <iostream>

using namespace std;

// 基类 Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};

// 基类 PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
}
};

// 派生类
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};

int main(void)
{
Rectangle Rect;
int area;

Rect.setWidth(5);
Rect.setHeight(7);

area = Rect.getArea();

// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;

// 输出总花费
cout << "Total paint cost: $" << Rect.getCost(area) << endl;

return 0;
}
1
2
3
4
当上面的代码被编译和执行时,它会产生下列结果:

Total area: 35
Total paint cost: $2450

虚继承

另外多继承(环状继承),A->D, B->D, C->(A,B),例如:

1
2
3
4
class D{......};
class B: public D{......};
class A: public D{......};
class C: public B, public A{.....};

这个继承会使D创建两个对象,要解决上面问题就要用虚拟继承格式

格式:class 类名: virtual 继承方式 父类名

1
2
3
4
class D{......};
class B: virtual public D{......};
class A: virtual public D{......};
class C: public B, public A{.....};

虚继承–(在创建对象的时候会创建一个虚表)在创建父类对象的时候

1
2
A:virtual public D
B:virtual public D

实例:

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
#include <iostream>

using namespace std;
//基类

class D
{
public:
D(){cout<<"D()"<<endl;}
~D(){cout<<"~D()"<<endl;}
protected:
int d;
};

class B:virtual public D
{
public:
B(){cout<<"B()"<<endl;}
~B(){cout<<"~B()"<<endl;}
protected:
int b;
};

class A:virtual public D
{
public:
A(){cout<<"A()"<<endl;}
~A(){cout<<"~A()"<<endl;}
protected:
int a;
};

class C:public B, public A
{
public:
C(){cout<<"C()"<<endl;}
~C(){cout<<"~C()"<<endl;}
protected:
int c;
};

int main()
{
cout << "Hello World!" << endl;
C c; //D, B, A ,C
cout<<sizeof(c)<<endl;
return 0;
}

c++模板

Posted on 2019-07-03 | In C++ , 其他 |
Words count in article: 965 | Reading time ≈ 4

C++ 模板

函数模板

模板函数定义的一般形式如下所示:

1
2
3
4
template <class type> ret-type func-name(parameter list)
{
// 函数的主体
}

在这里,type 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。

下面是函数模板的实例,返回两个数中的最大值:

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 <iostream>
#include <string>

using namespace std;

template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{

int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;

double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;

string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
Max(i, j): 39
Max(f1, f2): 20.7
Max(s1, s2): World

类模板

正如我们定义函数模板一样,我们也可以定义类模板。泛型类声明的一般形式如下所示:

1
2
3
4
5
template <class type> class class-name {
.
.
.
};

在这里,type 是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。

下面的实例定义了类 Stack<>,并实现了泛型方法来对元素进行入栈出栈操作:

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>

using namespace std;

template <class T>
class Stack {
private:
vector<T> elems; // 元素

public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};

template <class T>
void Stack<T>::push (T const& elem)
{
// 追加传入元素的副本
elems.push_back(elem);
}

template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
}

template <class T>
T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}

int main()
{
try {
Stack<int> intStack; // int 类型的栈
Stack<string> stringStack; // string 类型的栈

// 操作 int 类型的栈
intStack.push(7);
cout << intStack.top() <<endl;

// 操作 string 类型的栈
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
当上面的代码被编译和执行时,它会产生下列结果:

7
hello
Exception: Stack<>::pop(): empty stack#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>

using namespace std;

template <class T>
class Stack {
private:
vector<T> elems; // 元素

public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};

template <class T>
void Stack<T>::push (T const& elem)
{
// 追加传入元素的副本
elems.push_back(elem);
}

template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
}

template <class T>
T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}

int main()
{
try {
Stack<int> intStack; // int 类型的栈
Stack<string> stringStack; // string 类型的栈

// 操作 int 类型的栈
intStack.push(7);
cout << intStack.top() <<endl;

// 操作 string 类型的栈
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
7
hello
Exception: Stack<>::pop(): empty stack

矩阵类的实现

Posted on 2019-07-03 | In C++ , 其他 |
Words count in article: 2.4k | Reading time ≈ 15

矩阵类 正常存储:

Design a template class Matrix that has the following private member variables:

  1. int rows
  2. int columns
  3. vector values

where T is the type parameter.
Besides, it has the functions such that the main function runs correctly with the following output.

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
EXAMPLE OUTPUT
constructor 1
0 0 0
0 0 0
0 0 0
constructor 2
1 2 3
4 5 6
7 8 9
copy constructor
1 2 3
4 5 6
7 8 9
operator =
1 2 3
4 5 6
7 8 9
getColumn
2
5
8
getRow
4 5 6
concatenateRows
0 0 0
0 0 0
0 0 0
1 2 3
4 5 6
7 8 9
concatenateColumns
0 0 0 1 2 3
0 0 0 4 5 6
0 0 0 7 8 9
reshape
0 0 2
0 0 5
0 0 8
0 1 3
0 4 6
0 7 9
transpose
1 4 7
2 5 8
3 6 9
operator +
2 4 6
8 10 12
14 16 18
operator +
11 12 13
14 15 16
17 18 19
operator -
0 2 4
-2 0 2
-4 -2 0
operator -
-9 -8 -7
-6 -5 -4
-3 -2 -1
operator *
66 78 90
78 93 108
90 108 126
operator *
2 4 6
8 10 12
14 16 18
max
9
min
1
sum
45
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#include <iostream>
#include <string>
#include <vector>
using namespace std;

template <typename E>
class Matrix{
private:
int rows;
int columns;
vector<E> values;
public:
Matrix(int r, int c):rows(r),columns(c){
this->values.clear();
this->values.resize(r*c);
}
Matrix(int r, int c, vector<E>& v):rows(r),columns(c){
this->values.clear();
this->values.resize(r*c);
for(int i=0; i<r*c; i++)
values[i] = v[i];
}
Matrix(const Matrix & m)
{
this->columns = m.columns;
this->rows = m.rows;
this->values.clear();
this->values.resize(m.rows*m.columns);
for(int i=0; i<m.rows*m.columns; i++)
this->values[i] = m.values[i];
}
Matrix & operator = (const Matrix & m)
{
this->rows = m.rows;
this->columns = m.columns;
this->values.clear();
this->values.resize(m.rows*m.columns);
for(int i=0; i<m.rows*m.columns; i++)
this->values[i] = m.values[i];
return *this;
}
~Matrix()
{
this->values.clear();
}
void print()
{
for(int i=0; i<this->rows; i++)
{
for(int j=0; j<this->columns; j++)
cout << " " << values[i*this->columns+j];
cout << endl;
}
}
Matrix getColumn(int c)
{
Matrix rem(this->rows, 1);
for(int i=0; i<this->rows; i++)
rem.values[i] = this->values[i*this->columns+c-1];
return rem;
}
Matrix getRow(int r)
{
Matrix rem(1, this->columns);
for(int i=0; i<this->columns; i++)
rem.values[i] = this->values[(r-1)*this->columns+i];
return rem;
}
Matrix concatenateRows(const Matrix & m)
{
Matrix rem(this->rows+m.rows, this->columns);
for(int i=0; i<this->rows*this->columns; i++)
rem.values[i] = this->values[i];
for(int i=0; i<m.rows*m.columns; i++)
rem.values[this->rows*this->columns+i] = m.values[i];
return rem;
}
Matrix concatenateColumns(const Matrix & m)
{
Matrix rem(this->rows, this->columns+m.columns);
int z = 0;
for(int i=0; i<this->rows; i++)
{
for(int j=0; j<this->columns; j++)
rem.values[z++] = this->values[i*this->columns+j];
for(int j=0; j<m.columns; j++)
rem.values[z++] = m.values[i*m.columns+j];
}
return rem;
}
Matrix reshape(int r, int c)
{
Matrix rem(r, c);
E arr[r*c];
int z = 0;
for(int i=0; i<this->columns; i++)
{
for(int j=0; j<this->rows; j++)
{
arr[z++] = this->values[j*this->columns+i];
}
}
z = 0;
for(int i=0; i<c; i++)
{
for(int j=0; j<r; j++)
{
rem.values[j*c+i] = arr[z++];
}
}
return rem;
}
Matrix transpose()
{
Matrix rem(this->columns, this->rows);
for(int i=0; i<rem.rows; i++)
{
for(int j=0; j<rem.columns; j++)
{
rem.values[i*rem.columns+j] = this->values[j*this->columns+i];
}
}
return rem;
}
Matrix operator + (int n)
{
Matrix rem(this->rows, this->columns);
for(int i=0; i<this->rows*this->columns; i++)
{
rem.values[i] = this->values[i] + n;
}
return rem;
}
Matrix operator + (Matrix &n)
{
Matrix rem(this->rows, this->columns);
for(int i=0; i<this->rows*this->columns; i++)
{
rem.values[i] = this->values[i] + n.values[i];
}
return rem;
}
Matrix operator - (int n)
{
Matrix rem(this->rows, this->columns);
for(int i=0; i<this->rows*this->columns; i++)
{
rem.values[i] = this->values[i] - n;
}
return rem;
}
Matrix operator - (Matrix &n)
{
Matrix rem(this->rows, this->columns);
for(int i=0; i<this->rows*this->columns; i++)
{
rem.values[i] = this->values[i] - n.values[i];
}
return rem;
}
Matrix operator * (int n)
{
Matrix rem(this->rows, this->columns);
for(int i=0; i<this->rows*this->columns; i++)
{
rem.values[i] = this->values[i] * n;
}
return rem;
}
Matrix operator * (const Matrix & matrix2) const{
Matrix rem(this->rows, matrix2.columns);
for(int i=0; i<this->rows; i++)
{
for(int j=0; j<matrix2.columns; j++)
{
int sum = 0;
for(int x=0; x<this->columns; x++)
{
sum += this->values[i*this->columns+x]*matrix2.values[x*matrix2.columns+j];

}
rem.values[i*matrix2.columns+j] = sum;
}
}
return rem;
}
E& get(int r, int c)
{
int nr = r-1;
int nc = c-1;
return this->values[nr*this->columns+nc];
}
Matrix sum() const{
Matrix rem(1, this->columns);
if(this->rows == 1)
{
rem.columns = 1;
for(int i=0; i<this->columns; i++)
{
rem.values[0] += this->values[i];
}
}
else
{
for(int i=0; i<this->columns; i++)
{
for(int j=0; j<this->rows; j++)
{
rem.values[i] += this->values[j*this->columns+i];
}
}
}
return rem;
}
Matrix max()const {
Matrix rem(1, this->columns);
if(this->rows == 1)
{
rem.columns = 1;
for(int i=0; i<this->columns; i++)
{
rem.values[0] = rem.values[0]>this->values[i] ? rem.values[0] : this->values[i];
}
}
else
{
for(int i=0; i<this->columns; i++)
{
for(int j=0; j<this->rows; j++)
{
rem.values[i] = rem.values[i] > this->values[j*this->columns+i] ? rem.values[i]: this->values[j*this->columns+i];
}
}
}
return rem;
}
Matrix min()const {
Matrix rem(1, this->columns);
if(this->rows == 1)
{
rem.columns = 1;
rem.values[0] = this->values[0];
for(int i=0; i<this->columns; i++)
{
rem.values[0] = rem.values[0]<this->values[i] ? rem.values[0] : this->values[i];
}
}
else
{
for(int i=0; i<this->columns; i++)
{
rem.values[i] = this->values[i];
for(int j=0; j<this->rows; j++)
{
rem.values[i] = rem.values[i] < this->values[j*this->columns+i] ? rem.values[i]: this->values[j*this->columns+i];
}
}
}
return rem;
}
};

int main() {
cout << "constructor 1" << endl;
Matrix<double> matrix1(3, 3);
matrix1.print();

const double values1[] = {
1, 2, 3,
4, 5, 6,
7, 8, 9,
};
vector<double> values2;
for (int i = 0; i < 9; ++ i) {
values2.push_back(values1[i]);
}

cout << "constructor 2" << endl;
Matrix<double> matrix2(3, 3, values2);
matrix2.print();

cout << "copy constructor" << endl;
Matrix<double> matrix3 = matrix2;
matrix3.print();

cout << "operator =" << endl;
matrix3.get(1, 1) = 10.0;
matrix3 = matrix2;
matrix3.print();

cout << "getColumn" << endl;
matrix2.getColumn(2).print();
cout << "getRow" << endl;
matrix2.getRow(2).print();

cout << "concatenateRows" << endl;
matrix1.concatenateRows(matrix2).print();
cout << "concatenateColumns" << endl;
matrix1.concatenateColumns(matrix2).print();

cout << "reshape" << endl;
matrix1.concatenateColumns(matrix2).
reshape(6, 3).print();

cout << "transpose" << endl;
matrix2.transpose().print();

cout << "operator +" << endl;
(matrix2 + matrix2).print();
cout << "operator +" << endl;
(matrix2 + 10).print();
cout << "operator -" << endl;
(matrix2.transpose() - matrix2).print();
cout << "operator -" << endl;
(matrix2 - 10).print();

cout << "operator *" << endl;
(matrix2.transpose() * matrix2).print();
cout << "operator *" << endl;
(matrix2 * 2).print();

cout << "max" << endl;
cout << matrix2.max().max().get(1, 1) << endl;
cout << "min" << endl;
cout << matrix2.min().min().get(1, 1) << endl;
cout << "sum" << endl;
cout << matrix2.sum().sum().get(1, 1) << endl;
}

矩阵类 稀疏矩阵存储:

Design a class Sparse that implements interface Matrix: Sparse should has the following public object functions in addition:

A constructor Sparse(int rows, int column), which initializes all elements in the matrix to 0's.

A function Sparse Sparse::operator * (Sparse & sparse2), which returns the product of two sparse matrixes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
EXAMPLE INPUT
1000000 2000000 3000000

1 1 10
1 2000000 50
1000000 2000000 20

1 3000000 30
2000000 1 40
1 1 -10
EXAMPLE OUTPUT
(1,1,1900)
(1,3000000,300)
(1000000,1,800)
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <iostream>
#include <vector>
using namespace std;

class Entry
{
public:
int row;
int column;
double value;
};

class Matrix
{
public:
virtual int size(int dimension) const = 0;

virtual void set(int row, int column,
double value) = 0;

virtual double get(int row, int column)
const = 0;

virtual void print() = 0;

};


class Sparse : public Matrix
{
private:
int rows;
int column;
vector<Entry> mmatrix;
public:
Sparse(int rows, int column)
{
this->rows = rows;
this->column = column;
}
virtual int size(int dimension) const
{
if(dimension == 1) return this->rows;
if(dimension == 2) return this->column;
}
virtual void set(int row, int column, double value)
{
int flag = 0;
for(int i=0; i<mmatrix.size(); i++)
{
if(mmatrix[i].row == row && mmatrix[i].column == column)
{
flag = 1;
mmatrix[i].value = value;
break;
}
}
if(!flag)
{
Entry rem;
rem.row = row;
rem.column = column;
rem.value = value;
mmatrix.push_back(rem);
}
}
virtual double get(int row, int column)const
{
for(int i=0; i<mmatrix.size(); i++)
{
if(mmatrix[i].row == row && mmatrix[i].column == column)
{
return mmatrix[i].value;
}
}
return -1;
}
virtual void print()
{
for(int i=0; i<mmatrix.size()-1; i++)
{
for(int j=0; j<mmatrix.size()-1-i; j++)
{
if(mmatrix[j].row>mmatrix[j+1].row)
{
Entry temp = mmatrix[j];
mmatrix[j] = mmatrix[j+1];
mmatrix[j+1] = temp;
}
else if(mmatrix[j].row==mmatrix[j+1].row)
{
if(mmatrix[j].column>mmatrix[j+1].column)
{
Entry temp = mmatrix[j];
mmatrix[j] = mmatrix[j+1];
mmatrix[j+1] = temp;
}
}
}
}
for(int i=0; i<mmatrix.size(); i++)
{
cout << "(" << mmatrix[i].row << "," << mmatrix[i].column << "," << mmatrix[i].value << ")" << endl;
}
}
Sparse operator * (Sparse &sparse2)
{
Sparse rem(this->rows, sparse2.column);
vector<Entry> ttt;
for(int i=0; i<this->mmatrix.size(); i++)
{
for(int j=0; j<sparse2.mmatrix.size(); j++)
{
if(this->mmatrix[i].column == sparse2.mmatrix[j].row)
{
Entry rrr;
rrr.row = this->mmatrix[i].row;
rrr.column = sparse2.mmatrix[j].column;
rrr.value = this->mmatrix[i].value * sparse2.mmatrix[j].value;
ttt.push_back(rrr);
}

}
}
int num[ttt.size()] = {0};
for(int i=0; i<ttt.size(); i++)
{
if(num[i]==0)
{
int sum = ttt[i].value;
num[i] = 1;
for(int j=0; j<ttt.size(); j++)
{
if(ttt[i].row == ttt[j].row && ttt[i].column == ttt[j].column && i != j)
{
sum += ttt[j].value;
num[j] = 1;
}
}
if(sum != 0)
{
Entry zzz;
zzz.row = ttt[i].row;
zzz.column = ttt[i].column;
zzz.value = sum;
rem.mmatrix.push_back(zzz);
}
}
}
return rem;
}
};


void print(Matrix & matrix) {
matrix.print();
}

void readAndSetElement(Matrix & matrix) {
int row;
int column;
double value;
cin >> row >> column >> value;
matrix.set(row, column, value);
}

void readAndSetMultipleElements(Matrix & matrix, int count) {
for (int i = 0; i < count; ++ i) {
readAndSetElement(matrix);
}
}

int main() {
int rows;
int columns;
int & rows2 = columns;
int columns2;

cin >> rows >> columns >> columns2;

Sparse sparse1(rows, columns);
readAndSetMultipleElements(sparse1, 3);

Sparse sparse2(rows2, columns2);
readAndSetMultipleElements(sparse2, 3);

Sparse sparse3 = sparse1 * sparse2;
print(sparse3);
}

c++异常处理

Posted on 2019-07-03 | In C++ , 其他 |
Words count in article: 1.5k | Reading time ≈ 7

C++ 异常处理

C++ 异常处理涉及到三个关键字:try、catch、throw。

  • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
  • catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
  • try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
  • 如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
try
{
// 保护代码
}catch( ExceptionName e1 )
{
// catch 块
}catch( ExceptionName e2 )
{
// catch 块
}catch( ExceptionName eN )
{
// catch 块
}

如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常。

抛出异常

您可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。

以下是尝试除以零时抛出异常的实例:

1
2
3
4
5
6
7
8
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}

捕获异常

catch 块跟在 try 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。

1
2
3
4
5
6
7
try
{
// 保护代码
}catch( ExceptionName e )
{
// 处理 ExceptionName 异常的代码
}

上面的代码会捕获一个类型为 ExceptionName 的异常。如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 …,如下所示:

1
2
3
4
5
6
7
try
{
// 保护代码
}catch(...)
{
// 能处理任何异常的代码
}
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 <iostream>
using namespace std;

double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}

int main ()
{
int x = 50;
int y = 0;
double z = 0;

try {
z = division(x, y);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}

return 0;
}

由于我们抛出了一个类型为 const char 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char。当上面的代码被编译和执行时,它会产生下列结果:

Division by zero condition!

C++ 标准的异常

C++ 提供了一系列标准的异常,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的。

两个异常相关库分别是:

1
2
#include <stdexcept>
#include <exception>

定义新的异常

您可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常:

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
#include <iostream>
#include <exception>
using namespace std;

struct MyException : public exception
{
const char * what () const throw ()
{
return "C++ Exception";
}
};

int main()
{
try
{
throw MyException();
}
catch(MyException& e)
{
std::cout << "MyException caught" << std::endl;
std::cout << e.what() << std::endl;
}
catch(std::exception& e)
{
//其他的错误
}
}
1
2
3
4
5
这将产生以下结果:

MyException caught
C++ Exception
在这里,what() 是异常类提供的一个公共方法,它已被所有子异常类重载。这将返回异常产生的原因。
Read more »

重载类型转换运算符

Posted on 2019-07-03 | In C++ , 其他 |
Words count in article: 151 | Reading time ≈ 1

使用方法就是在对应类里面编写operator对应成员函数:
例如:

1
2
3
operator string() {} 
operator int () {}
operator double() {}

可以利用重载类型转换运算符,编写一个类来实现可以存储double类型和string类型的输入,并且输出时候仍然输出对应的double或者string

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
#include <iostream>
#include <string>
using namespace std;

class intandstring{
private:
int intnum;
string stringnum;
public:
intandstring(int number):intnum(number){}
intandstring(string number):stringnum(number){}
operator string(){
return stringnum;
}
operator int(){
return intnum;
}
};

int main()
{
string aaa;
cin >> aaa;
intandstring a(aaa);
cout << (string)a;
}
1…202122…38
zzm99

zzm99

372 posts
40 categories
3 tags
GitHub
0%
© 2020 zzm99 | Site words total count: 409.1k
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4