首页 > 编程语言 >实验3 类和对象——基础编程2

实验3 类和对象——基础编程2

时间:2024-11-09 20:09:00浏览次数:1  
标签:std const string 对象 编程 int 实验 vectorInt include

一、实验目的

 加深对类的组合机制的理解,会正确使用C++正确定义,使用组合类

理解深复制,浅复制

练习标准库string ,vector的用法,能基于问题场景灵活使用

针对具体问题场景,练习运用面向对象思维进行设计,合理设计,组合类(自定义/标准库),编程解决实际问题。

二、实验准备

 系统复习浏览以下内容

类的抽象,设计

组合类:要解决的问题场景,定义和使用方法

数据共享,保护

标准库string ,vector的用法

 

三、实验内容

 

1. 实验任务1

代码:button.hpp

 1 #pragma once 
 2 #include <iostream>
 3 #include<string>
 4 
 5 using std::string;
 6 using std::cout;
 7 
 8 class Button{
 9     public:
10         Button(const string &text);
11         string get_label()const;
12         void click();
13     private:
14         string label;
15 };
16 Button::Button(const string &text):label {text}{
17 
18 } 
19 
20 inline string Button::get_label()const {
21 return label;}
22 
23 void Button::click(){
24     
25     cout<<"Button "<<label<<"clicked\n";
26     
27 }

window.hpp

 1 #pragma once 
 2 #include"button.hpp"
 3 #include<vector>
 4 #include<iostream>
 5 
 6 
 7 using std::vector;
 8 using std::cout;
 9 using std::endl;
10 
11 class Window{
12     public:
13         Window(const string &win_title);
14         void display()const;
15         void close();
16         void add_button(const string &label);
17     private:
18         string title;
19         vector <Button> buttons;
20         
21 };
22 Window::Window(const string &win_title):title{win_title}{
23 buttons.push_back(Button("close "));}
24 
25 
26 inline void Window::display()const{
27 string s(40,'*');
28 cout<<s<<endl;
29 cout<<"window title:"<<title<<endl;
30 cout<<"It has "<<buttons.size()<<" buttons:"<<endl;
31 for(const auto&i:buttons)
32 cout<<i.get_label()<<"button"<<endl;
33 cout<<s<<endl;
34 
35 
36 }
37 void Window::close(){
38     cout<<"close window"<<" '"<<title<<"'"<<endl;
39     buttons.at(0).click();
40     
41 }
42 
43 void Window::add_button(const string &label){
44     buttons.push_back(Button(label));
45     
46 }

task1.cpp

 1 #include"window.hpp"
 2 #include<iostream>
 3 
 4 
 5 using std::cout;
 6 using std::cin;
 7 
 8 void test(){
 9     
10     Window w1("new window");
11     w1.add_button("maximize ");
12     w1.display();
13     w1.close();
14 }
15 
16 int main(){
17     cout<<"用组合类模拟简单GUI:\n";
18     test();
19 }

 

 

 

运行截图:

 

 

 

问题回答:

问题一:这个示例代码中,自定义了2个类,一个Button,一个Window类,使用到了标准库的string和vector类。其中string和Button类,string 和Window类,vector和Window类存在组合关系

问题二:在Button类当中,  get_label()函数被标记为const,这是合适的,因为它只返回标签而不修改任何数据。或者设置成inline,因为它简单且频繁调用。在Windows类当中,disliay()函数可以被标记为const,因为它只显示窗口信息而不改变。但不应设置成inline,因为它的代码量太大了。close()不应设置成为const因为它会改变

问题三:string s(40,'*');是创建一个名为s 的字符串,长度为40,且所有字符均为*

 

 

2. 实验任务2

代码:

 1 #include<iostream>
 2 
 3 #include<vector>
 4 
 5 using namespace std;
 6 void output1(const vector<int> &v){
 7     for(auto &i:v)
 8         cout<<i<<",";
 9     cout<<"\b\b \n";
10     
11 }
12 
13 void output2(const vector <vector<int>> v){
14     for(auto &i:v){
15         for(auto &j:i)
16             cout<<j<<", ";
17         cout<<"\b\b \n";
18         
19     }
20     
21 }
22 void test1(){
23     vector<int> v1(5,42);
24     const vector<int> v2(v1);
25     
26     v1.at(0)=-999;
27     cout<<"v1: "; output1(v1);
28     cout<<"v2: ";output1(v2) ;
29     cout<<"v1.at(0) = "<<v1.at(0)<<endl;
30     cout<<"v2.at(0) = "<<v2.at(0)<<endl;
31     
32 }
33 
34 void test2(){
35     vector<vector<int>> v1{{1,2,3},{4,5,6,7}};
36     const vector <vector<int>> v2(v1);
37     
38     v1.at(0).push_back(-999);
39     cout<<"v1: \n";output2(v1);
40     cout<<"v2: \n";output2(v2);
41     
42     vector<int> t1=v1.at(0);
43     cout<<t1.at(t1.size()-1)<<endl;
44     
45     const vector<int> t2=v2.at(0);
46     cout<<t2.at(t2.size()-1)<<endl;
47     
48 }
49 
50 int main(){
51     cout<<"测试1:\n";
52     test1();
53     cout<<"测试2:\n";
54     test2();
55 }

 

 

运行截图

 

 

 

 

问题回答:

问题一:

vector<int> v1(5,42);
const vector<int> v2(v1);

v1.at(0)=-999;

 这个代码是定义一个常量引用的整型向量v1,内部包含5个元素,且全部初始化值为42,把v1的值拷贝给了v2,v2成为了不可变的一维向量;而后又将第一个元素改成了-999;

问题二:

vector<vector<int>> v1{{1,2,3},{4,5,6,7}};
const vector <vector<int>> v2(v1);

v1.at(0).push_back(-999);

  这个代码是定义一个常量的二维整型向量v1,{{1,2,3},{4,5,6,7}};表示v1包含两个内层向量

第一个内层向量是{1,2,3},包含三个元素。第二个内层向量是{4,5,6,7},包含四个元素。同样把v1的值拷贝给了v2,v2成为了不可变的二维向量;

而后又把-999增加到了v1的第一层的末尾,变成了v1{{1,2,3,-999},{4,5,6,7}};

3. 实验任务3

代码:

 vectorInt.hpp

 1 #pragma once
 2 
 3 #include <iostream>
 4 #include <cassert>
 5 
 6 using std::cout;
 7 using std::endl;
 8 
 9 
10 class vectorInt{
11 public:
12     vectorInt(int n);
13     vectorInt(int n, int value);
14     vectorInt(const vectorInt &vi);
15     ~vectorInt();
16 
17     int& at(int index);
18     const int& at(int index) const;
19 
20     vectorInt& assign(const vectorInt &v);
21     int get_size() const;
22 
23 private:
24     int size;
25     int *ptr;      
26 };
27 
28 vectorInt::vectorInt(int n): size{n}, ptr{new int[size]} {
29 }
30 
31 vectorInt::vectorInt(int n, int value): size{n}, ptr{new int[size]} {
32     for(auto i = 0; i < size; ++i)
33         ptr[i] = value;
34 }
35 
36 vectorInt::vectorInt(const vectorInt &vi): size{vi.size}, ptr{new int[size]} {
37     for(auto i = 0; i < size; ++i)
38         ptr[i] = vi.ptr[i];
39 }
40 
41 vectorInt::~vectorInt() {
42     delete [] ptr;
43 }
44 
45 const int& vectorInt::at(int index) const {
46     assert(index >= 0 && index < size);
47 
48     return ptr[index];
49 }
50 
51 int& vectorInt::at(int index) {
52     assert(index >= 0 && index < size);
53 
54     return ptr[index];
55 }
56 
57 vectorInt& vectorInt::assign(const vectorInt &v) {  
58     delete[] ptr;     
59 
60     size = v.size;
61     ptr = new int[size];
62 
63     for(int i = 0; i < size; ++i)
64         ptr[i] = v.ptr[i];
65 
66     return *this;
67 }
68 
69 int vectorInt::get_size() const {
70     return size;
71 }

task3.cpp

 1 #include "vectorInt.hpp"
 2 #include <iostream>
 3 
 4 using std::cin;
 5 using std::cout;
 6 
 7 void output(const vectorInt &vi) {
 8     for(auto i = 0; i < vi.get_size(); ++i)
 9         cout << vi.at(i) << ", ";
10     cout << "\b\b \n";
11 }
12 
13 
14 void test1() {
15     int n;
16     cout << "Enter n: ";
17     cin >> n;
18 
19     vectorInt x1(n);
20     for(auto i = 0; i < n; ++i)
21         x1.at(i) = i*i;
22     cout << "x1: ";  output(x1);
23 
24     vectorInt x2(n, 42);
25     vectorInt x3(x2);
26     x2.at(0) = -999;
27     cout << "x2: ";  output(x2);
28     cout << "x3: ";  output(x3);
29 }
30 
31 void test2() {
32     const vectorInt  x(5, 42);
33     vectorInt y(10, 0);
34 
35     cout << "y: ";  output(y);
36     y.assign(x);
37     cout << "y: ";  output(y);
38     
39     cout << "x.at(0) = " << x.at(0) << endl;
40     cout << "y.at(0) = " << y.at(0) << endl;
41 }
42 
43 int main() {
44     cout << "测试1: \n";
45     test1();
46 
47     cout << "\n测试2: \n";
48     test2();
49 }

 

 

运行截图:

 

 

 

 问题回答:

 问题一:  vectorInt(const vectorInt &vi);的实现是深复制。复制一个vectorInt对象,会分配新的内存并复制原对象中的元素,包括指针。

问题二:vectorInt类中,这两个at()接口,如果返回值类型改成int而非int&(相应地,实现部分也 同步修改),测试代码还能正确运行吗?能正常运行,但也意味着返回的是元素的值而不是对该值的引用。在这种情况下,调用 at() 的代码将能够正常运行,但会失去对原始数组元素的修改能力

如果把line18返回值类型前面的const掉,针对这个测试 代码,是否有潜在安全隐患?会存在安全隐患。返回一个非 const 引用可能会允许外部代码修改对象的内部数据结构。

问题三:assign() 方法的返回值类型可以改成vectorInt,但是每次调用之后返回的不是原对象的引用,而是一个副本。

 

 

4. 实验任务4

代码:

 

 1 #pragma once
 2 
 3 #include <iostream>
 4 #include <cassert>
 5 #include<cstring>
 6 
 7 using std::cout;
 8 using std::endl;
 9 
10 // 类Matrix的声明
11 class Matrix {
12 public:
13     Matrix(int n, int m);           // 构造函数,构造一个n*m的矩阵, 初始值为value
14     Matrix(int n);                  // 构造函数,构造一个n*n的矩阵, 初始值为value
15     Matrix(const Matrix &x);        // 复制构造函数, 使用已有的矩阵X构造
16     ~Matrix();
17 
18     void set(const double *pvalue);         // 用pvalue指向的连续内存块数据按行为矩阵赋值
19     void clear();                           // 把矩阵对象的值置0
20     
21     const double& at(int i, int j) const;   // 返回矩阵对象索引(i,j)的元素const引用
22     double& at(int i, int j);               // 返回矩阵对象索引(i,j)的元素引用
23     
24     int get_lines() const;                  // 返回矩阵对象行数
25     int get_cols() const;                   // 返回矩阵对象列数
26 
27     void display() const;                    // 按行显示矩阵对象元素值
28 
29 private:
30     int lines;      // 矩阵对象内元素行数
31     int cols;       // 矩阵对象内元素列数
32     double *ptr;
33 };
34 
35 Matrix::Matrix(int n, int m):lines{n},cols{m},ptr{new double[n*m]}{
36  
37 }           // 构造函数,构造一个n*m的矩阵, 初始值为value
38    Matrix::Matrix(int n) :lines{n},cols{n}, ptr{new double[n*n] }{}
39                  // 构造函数,构造一个n*n的矩阵, 初始值为value
40   Matrix::Matrix(const Matrix &x):lines{x.lines},cols(x.cols),ptr(new double[x.lines*x.cols]){
41   std::memcpy(ptr,x.ptr,lines*cols*sizeof(double));
42               }        // 复制构造函数, 使用已有的矩阵X构造
43     Matrix::~Matrix(){delete [] ptr;
44     }
45 
46     void Matrix::set(const double *pvalue){
47     for (auto i = 0; i < lines; ++i) {
48         for (auto j = 0; j < cols; ++j) {
49             ptr[i * cols + j] = pvalue[i * cols + j];
50         }
51     }}
52         // 用pvalue指向的连续内存块数据按行为矩阵赋值
53     void Matrix::clear(){std::memset(ptr ,0,lines*cols*sizeof(double));}                           // 把矩阵对象的值置0
54     
55     const double& Matrix::at(int i, int j) const {
56     assert(i >= 0 && i < lines && j >= 0 && j < cols);
57     return ptr[i * cols + j];
58 }   // 返回矩阵对象索引(i,j)的元素const引用
59     double& Matrix::at(int i, int j){
60     assert(i >= 0 && i < lines && j >= 0 && j < cols);
61     return ptr[i * cols + j];
62 }               // 返回矩阵对象索引(i,j)的元素引用
63     
64     int Matrix::get_lines() const{
65     return lines;}                  // 返回矩阵对象行数
66     int Matrix::get_cols() const{
67     return cols;}                  // 返回矩阵对象列数
68 
69     void Matrix::display() const{
70             for (auto i =0;i<lines;++i){
71            for(auto j =0;j<cols ;++j){
72                    cout<<at(i,j);
73                     if (j < cols - 1) {
74                 cout << ",";  }
75                    cout<<endl;}                 // 按行显示矩阵对象元素值
76 }

 

 

运行截图:

 

 

 

 

 

 

 

5. 实验任务5

代码:

 

 1 #ifndef USER_HPP
 2 #define USER_HPP
 3 
 4 #include <iostream>
 5 #include <string>
 6 #include <limits>
 7 
 8 class User {
 9 private:
10     std::string name;     // 用户名
11     std::string password; // 密码
12     std::string email;    // 邮箱
13 
14 public:
15     // 构造函数
16     User(const std::string& name0, const std::string& password0 = "123456", const std::string& mail0 = "")
17         : name(name0), password(password0), email(mail0) {}
18 
19     // 接口1,用来提示设置邮箱
20     void set_email() {
21     std::string test_email;
22     
23     while (true) { 
24         std::cout << "Enter email address: ";
25         std::cin >> test_email;
26 
27         if (test_email.find('@') == std::string::npos) {
28             std::cout << "Illegal email. Please re-enter email: ";
29         } else {
30             email = test_email;
31             std::cout << "Email is set successfully..." << std::endl;
32             break; 
33         }
34     }
35 }
36 
37     // 接口2,用来修改密码
38     void change_password() {
39         std::string old_password, new_password; // 定义 new_password
40         int attempts = 0;
41         const int max_attempts = 3;
42 
43         while (attempts < max_attempts) {
44             std::cout << "Enter old password: ";
45             std::cin >> old_password;
46            
47             if (old_password == password) {
48                 std::cout << "Enter new password: ";
49                 std::cin >> new_password;
50                 password = new_password;
51                 std::cout << "new password is set successfully..." << std::endl;
52                 return;
53             } else {
54                 attempts++;
55               
56                 if (attempts == max_attempts) {
57                     std::cout << "password input error. Please try after a while." << std::endl;
58                 } else {
59                     std::cout << "password input error. Please re-enter again." << std::endl;
60                 }
61                 
62             }
63         }
64     }
65 
66     // 接口3,用来打印用户信息
67     void display() const {
68         std::cout << "name: " << name << "\n";
69         std::cout << "pass: ";
70         for (int i = 0; i < password.length(); ++i) {
71             std::cout << "*"; 
72         }
73         std::cout << std::endl;
74         std::cout << "email: " << email << std::endl;
75     }
76 };
77 
78 #endif 

 

 

运行截图:

 

 

 

 

 

 

 

6. 实验任务6

代码:

 data.h

 1 #pragma once
 2 class Date 
 3 {
 4 private:
 5     int year;
 6     int month;
 7     int day;
 8     int totalDays;
 9 public:
10    Date(int year, int month, int day);
11    int getYear()const { return year; }
12    int getMonth() const { return month; }
13    int getDay()const { return day; }
14    int getMaxDay()const;
15    bool isLeapYear()const 
16   {
17       return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
18   }
19   void show()const;
20  int distance(const Date& date)const 
21   {
22        return totalDays - date.totalDays;
23     }
24  };

data.cpp

 1 #include"data.h"
 2 #include<iostream>
 3 #include<cstdlib>
 4  
 5 using namespace std;
 6  
 7 namespace 
 8  {
 9 const int DAYS_BEFORE_MONTH[] = { 0,31,59,90,120,151,181,212,243,273,304,334,365 };
10  }
11 Date::Date(int year, int month, int day) :year(year), month(month), day(day) 
12  {
13     if (day <= 0 || day > getMaxDay()) 
14     {
15         cout << "Invalid date: ";
16         show();
17         cout << endl;
18         exit(1);
19     }
20     int years = year - 1;
21     totalDays = year * 365 + years / 4 - years / 100 + years / 400 + DAYS_BEFORE_MONTH[month - 1] + day;
22     if (isLeapYear() && month > 2) totalDays++;
23 }
24 int Date::getMaxDay()const 
25 {
26     if (isLeapYear() && month == 2)
27          return 29;
28     else
29          return DAYS_BEFORE_MONTH[month] - DAYS_BEFORE_MONTH[month - 1];
30 }
31 void Date::show() const 
32 {
33     cout << getYear() << "-" << getMonth() << "-" << getDay();
34 }

account.h

 1 #pragma once
 2 #include"data.h"
 3 #include<string>
 4 
 5 class SavingsAccount 
 6 {
 7 private:
 8     std::string id;
 9     double balance;
10     double rate;
11     Date  lastDate;
12     double accumulation;
13     static double total;
14     void record(const Date& date, double amount, const std::string& desc);
15  
16     void error(const std::string& msg)const;
17 
18     double accumulate(const Date& date) const 
19     {
20     return accumulation + balance * date.distance(lastDate);
21  
22     }
23 public:
24     SavingsAccount(const Date& date, const std::string& id, double rate);
25 
26     const std::string& getId() const { return id; }
27     double getBalance() const { return balance; }
28 
29     double getRate() const { return rate; }
30 
31     static double getTotal() { return total; }
32 
33     void deposit(const Date& date, double amount, const std::string& desc);
34 
35     void withdraw(const Date& date, double amount, const std::string& desc);
36 
37     void settle(const Date& date);
38 
39     void show() const;
40  };

account.cpp

 1 #include "account.h"
 2 #include<cmath>
 3 #include<iostream>
 4  
 5 using namespace std;
 6 
 7 double SavingsAccount::total = 0;
 8 
 9 SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate) : id(id), balance(0), rate(rate), lastDate(date), accumulation(0) 
10 {
11     date.show();
12     cout << "\t#" << id << "  created" << endl;
13  }
14 
15 void SavingsAccount::record(const Date& date, double amount, const string& desc) 
16 {
17     accumulation = accumulate(date);
18  
19     lastDate = date;
20 
21     amount = floor(amount * 100 + 0.5) / 100; //保留小数点后两位
22     balance += amount;
23     total += amount;
24     date.show();
25  
26      cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
27  
28  }
29 void SavingsAccount::error(const string& msg)const 
30 {
31     cout << "Error(#" << id << "):" << msg << endl;
32 
33 }
34 
35  void SavingsAccount::deposit(const Date& date, double amount, const string& desc) 
36 {
37     record(date, amount, desc);
38  
39 }
40 
41  void SavingsAccount::withdraw(const Date& date, double amount, const string& desc) 
42  {
43     if (amount > getBalance())
44         error("not enough money");
45     else
46         record(date, -amount, desc);
47 
48  }
49  
50  void SavingsAccount::settle(const Date& date) 
51 {
52 
53     double interest = accumulate(date) * rate / date.distance(Date(date.getYear() - 1, 1, 1)); //计算年息
54 
55     if (interest != 0)
56  
57          record(date, interest, "interest");
58 
59      accumulation = 0;
60  
61 }
62 
63 void SavingsAccount::show() const 
64 {
65     cout << id << "\tBalance:" << balance;
66  }

6_25.cpp

 1 #include"account.h"
 2 #include<iostream>
 3 
 4 using namespace std;
 5  
 6 int main() 
 7 {
 8     Date date(2008, 11, 1);
 9  
10     SavingsAccount accounts[] = 
11     {
12     SavingsAccount(date, "03755217", 0.015),
13     SavingsAccount(date, "02342342", 0.015)
14     };
15     const int n = sizeof(accounts) / sizeof(SavingsAccount);
16 
17     accounts[0].deposit(Date(2008, 11, 5), 5000, "salary");
18     accounts[1].deposit(Date(2008, 11, 25), 10000, "sell stock 0323");
19     accounts[0].deposit(Date(2008, 12, 5), 5500, "salary");
20     accounts[1].withdraw(Date(2008, 12, 20), 4000, "buy a laptop");
21 
22    cout << endl;
23     for (int i = 0; i < n; i++) 
24     {
25         accounts[i].settle(Date(2009, 1, 1));
26         accounts[i].show();
27         cout << endl;
28     }
29     cout << "Total: " << SavingsAccount::getTotal() << endl;
30     return 0;
31  }

 

 

运行截图:

 

 

标签:std,const,string,对象,编程,int,实验,vectorInt,include
From: https://www.cnblogs.com/andongni51/p/18524347

相关文章

  • 实验3
    任务1:button.hpp:1#pragmaonce23#include<iostream>4#include<string>56usingstd::string;7usingstd::cout;89classButton{10public:11Button(conststring&text);12stringget_label()const;1......
  • 《人工智能导论》实验1-动物识别系统
    实现思路总结:该程序通过规则推理系统实现了一个简单的知识推理引擎,核心目标是根据已有事实和规则库中的规则,不断推导出新的结论,直到得出最终结论或无法继续推理为止。1.规则(Rule)类:规则类是推理引擎的核心部分,每条规则包含:前提条件(pre):该规则的前提条件(即规则生效所需的......
  • 天天学编程Day11
    每日一道编程题104.二叉树的最大深度classSolution{public:intans=0;intmaxDepth(TreeNode*root){dfs(root,1);returnans;}//使用深度优先遍历遍历二叉树记录路径长度不断更新全局变量最长长度//遍历完成后ans即为......
  • 4.1 WINDOWS XP,ReactOS对象与对象目录----1
    系列文章目录文章目录系列文章目录4.1对象与对象目录OBJECT_HEADERObpLookupEntryDirectory()NtCreateTimer()4.1对象与对象目录“对象(Object)”这个词现在大家都已耳熟能详了,但是对象到底是什么呢?广义地说,对象就是“目标”,行为的目标,或者说行为的受体。所以,广......
  • 面对对象编程:OOP类与对象详细笔记
    文章目录类类的概念:类的定义:类的属性(成员变量):**类的方法(成员方法):**对象对象的创建与使用:this关键字:构造方法:类与对象的关系:封装:无参方法有参方法类类的概念:类是一种用户定义的数据类型,它描述了一组具有相同特性和行为的对象。类定义了对象的状态(属性)和行为(方法)。......
  • 实验三c
    任务一实验代码button.hpp1#pragmaonce23#include<iostream>4#include<string>56usingstd::string;7usingstd::cout;89//按钮类10classButton{11public:12Button(conststring&text);13stringget_label()con......
  • 实验三
    实验1代码:1#pragmaonce23#include<iostream>4#include<string>56usingstd::string;7usingstd::cout;89//按钮类10classButton{11public:12Button(conststring&text);13stringget_label()const;14vo......
  • 退休后编程?
    也算半退休状态,学习新的编程语言基本上不可能了,没精力也没用武之地。但是,我觉得有些代码,还会一直写下去。①VBA,我估计写了10年以上VB代码,用VB干过各种事情,应用软件,Web终端,通信的,办公的,共享软件……没想到,老了以后,发现可能会一直用下去,还是vb代码。如果我评价哪个语言最有价值,我认......
  • PHP、Java、Python、C、C++ 这几种编程语言都各有什么特点或优点?
    相信每一个计算机科班出身的同学或许都有这样的经历:在大三的某一天,仿佛打通了全身筋脉一般把三年的所学:“数电里的与非门——[计算机体系结构]——汇编语言——C语言——C++语言——Java语言”。所有知识全部串联了起来。所有这些语言的出现都仿佛都有了必然性和追根溯源的历史......
  • Rust-AOP编程实战
    文章本天成,妙手偶得之。粹然无疵瑕,岂复须人为?君看古彝器,巧拙两无施。汉最近先秦,固已殊淳漓。胡部何为者,豪竹杂哀丝。后夔不复作,千载谁与期?——《文章》宋·陆游【哲理】文章本是不加人工,天然而成的,是技艺高超的人在偶然间所得到的。其实作者所说的“天成”,并不就是大自然的......