Categories
不学无术

C#的get和set

初学初学C#,发现跟Java没啥区别:)

//Class
public class TestClass
{
private double _numberA = 0;
public double NubmerA
{
get { return _numberA;}
set { _numberA = value;}
}
}
//Main
public class MainClass
{
public static void Main()
{
TestClass t = new TestClass();
r.NumberA = 123.456; // Derived class property
Console.WriteLine("Number in the derived class is " + d1.NumberA);
}
}

Categories
不学无术

C# override重写与new隐藏的区别

C#比java多一个new隐藏的功能。C# override重写相当于java中没有关键字的方法重写。所以java中方法是没有隐藏功能的。
C# override重写,是指对父类中的虚方法(标记virtual)或抽象方法(标记为abstract)进行重写,实现新的功能,它必须与父类方法的签名完全一致,而且与父类方法的可访问性也必须一致 new隐藏,是指在子类中重新定义一个签名与父类的方法相同的方法,这个方法可以不用new修饰,只是编译时会弹出一个警告信息:如果是有意隐藏,请使用关键字new。

using System;
public class Test
{
public static void Main(String[] args){
A a=new B();
Console.Write(a.getVal()); // 输出:A 虽然实例是B,但是执行的却是A的方法
a=new C();
Console.Write(a.getVal()); // 输出:C 实例是C,而且C重写的A的方法,所以执行C的方法
B b=new B();
Console.Write(b.getVal()); // 输出:B 只有定义和实例都是B,才执行B的方法
}
}
class A
{
// 要允许子类重写,方法必须标识为virtual 或 abstract
public virtual String getVal(){
return "A";
}
}
class B : A
{
// 隐藏父类方法 用new标识,不标识时会出现编译警告,不影响使用
public new String getVal(){
return "B";
}
}
class C : A
{
// 重写方法 必须用override标识
public override String getVal(){
return "C";
}
}

而在Java中,不存在隐藏父类方法这一概念,Java只有重写方法这一概念,同样的代码,在Java中执行和C#中执行截然不同:

class test
{
public static void main(String[] args)
{
A a=new B();
// 输出:B 实例是B,执行的也是B的方法(在C#中,此时应该输出A)
System.out.println(a.getVal());
a=new A();
// 输出:A 只有定义和实例都是A,才执行A的方法
System.out.println(a.getVal());
}
}
class A
{
public String getVal(){
return "A";
}
}
class B extends A
{
public String getVal(){
return "B";
}
}

Categories
不学无术

为什么operator

http://www.cnblogs.com/this-543273659/archive/2011/09/01/2161574.html
如果是重载双目操作符(即为类的成员函数),就只要设置一个参数作为右侧运算量,而左侧运算量就是对象本身。。。。。。
而 >>  或<< 左侧运算量是 cin或cout 而不是对象本身,所以不满足后面一点。。。。。。。。就只能申明为友元函数了。。。
如果一定要声明为成员函数,只能成为如下的形式:
ostream & operator<<(ostream &output)
{
return output;
}
所以在运用这个<<运算符时就变为这种形式了:data<<cout;
不合符人的习惯。

Categories
不学无术

c++中vector自定义排序的问题

http://blog.csdn.net/stone_sky/article/details/8471722

Categories
不学无术

C++异常机制

本文转载自 http://ticktick.blog.51cto.com/823160/191881
====================================================================
这两天我写了一个测试c++异常处理机制的例子,感觉有很好的示范作用,在此贴出来,给c++异常处理的初学者入门。本文后附有c++异常的知识普及,有兴趣者也可以看看。
下面的代码直接贴到你的console工程中,可以运行调试看看效果,并分析c++的异常机制。

  1. #include “stdafx.h”
  2. #include<stdlib.h>
  3. #include<crtdbg.h>
  4. #include <iostream>
  5. // 内存泄露检测机制
  6. #define _CRTDBG_MAP_ALLOC
  7. #ifdef _DEBUG
  8. #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
  9. #endif
  10. // 自定义异常类
  11. class MyExcepction
  12. {
  13. public:
  14.         // 构造函数,参数为错误代码
  15.         MyExcepction(int errorId)
  16.         {
  17.          // 输出构造函数被调用信息
  18.             std::cout << “MyExcepction is called” << std::endl;
  19.             m_errorId = errorId;
  20.         }
  21.         // 拷贝构造函数
  22.         MyExcepction( MyExcepction& myExp)
  23.         {
  24.          // 输出拷贝构造函数被调用信息
  25.             std::cout << “copy construct is called” << std::endl;
  26.             this->m_errorId = myExp.m_errorId;
  27.         }
  28.        ~MyExcepction()
  29.         {
  30.             // 输出析构函数被调用信息
  31.             std::cout << “~MyExcepction is called” << std::endl;
  32.         }
  33.        // 获取错误码
  34.         int getErrorId()
  35.         {
  36.             return m_errorId;
  37.         }
  38. private:
  39.         // 错误码
  40.         int m_errorId;
  41. };
  42. int main(int argc, char* argv[])
  43. {
  44.         // 内存泄露检测机制
  45.         _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
  46.         // 可以改变错误码,以便抛出不同的异常进行测试
  47.         int throwErrorCode = 110;
  48.        std::cout << ” input test code :” << std::endl;
  49.        std::cin >> throwErrorCode;
  50.        try
  51.        {
  52.             if ( throwErrorCode == 110)
  53.             {
  54.              MyExcepction myStru(110);
  55.                 // 抛出对象的地址 -> 由catch( MyExcepction*    pMyExcepction) 捕获
  56.                 // 这里该对象的地址抛出给catch语句,不会调用对象的拷贝构造函数
  57.                 // 传地址是提倡的做法,不会频繁地调用该对象的构造函数或拷贝构造函数
  58.                 // catch语句执行结束后,myStru会被析构掉
  59.                 throw    &myStru;
  60.             }
  61.             else if ( throwErrorCode == 119 )
  62.             {
  63.              MyExcepction myStru(119);
  64.                 // 抛出对象,这里会通过拷贝构造函数创建一个临时的对象传出给catch
  65.                 // 由catch( MyExcepction    myExcepction) 捕获
  66.                 // 在catch语句中会再次调用通过拷贝构造函数创建临时对象复制这里传过去的对象
  67.                 // throw结束后myStru会被析构掉
  68.                 throw    myStru;
  69.              }
  70.              else if ( throwErrorCode == 120 )
  71.              {
  72.                   // 不提倡这样的抛出方法
  73.                   // 这样做的话,如果catch( MyExcepction*    pMyExcepction)中不执行delete操作则会发生内存泄露
  74.                   // 由catch( MyExcepction*    pMyExcepction) 捕获
  75.                   MyExcepction * pMyStru = new MyExcepction(120);
  76.                   throw pMyStru;
  77.              }
  78.              else
  79.              {
  80.                   // 直接创建新对象抛出
  81.                   // 相当于创建了临时的对象传递给了catch语句
  82.                   // 由catch接收时通过拷贝构造函数再次创建临时对象接收传递过去的对象
  83.                   // throw结束后两次创建的临时对象会被析构掉
  84.                    throw MyExcepction(throwErrorCode);
  85.              }
  86.         }
  87.         catch( MyExcepction*    pMyExcepction)
  88.         {
  89.              // 输出本语句被执行信息
  90.                std::cout << “执行了 catch( MyExcepction*    pMyExcepction) ” << std::endl;
  91.              // 输出错误信息
  92.                std::cout << “error Code : ” << pMyExcepction->getErrorId()<< std::endl;
  93.             // 异常抛出的新对象并非创建在函数栈上,而是创建在专用的异常栈上,不需要进行delete
  94.             //delete pMyExcepction;
  95.         }
  96.         catch ( MyExcepction myExcepction)
  97.         {
  98.             // 输出本语句被执行信息
  99.             std::cout << “执行了 catch ( MyExcepction myExcepction) ” << std::endl;
  100.             // 输出错误信息
  101.             std::cout << “error Code : ” << myExcepction.getErrorId()<< std::endl;
  102.         }
  103.         catch(…)
  104.         {
  105.              // 输出本语句被执行信息
  106.              std::cout << “执行了 catch(…) ” << std::endl;
  107.              // 处理不了,重新抛出给上级
  108.              throw ;
  109.         }
  110.         // 暂停
  111.         int temp;
  112.         std::cin >> temp;
  113.        return 0;
  114. }

知识点: c++异常机制
一、 概述
C++自身有着非常强的纠错能力,发展到如今,已经建立了比较完善的异常处理机制。C++的异常情况无非两种,一种是语法错误,即程序中出现了错误的语句,函数,结构和类,致使编译程序无法进行。另一种是运行时发生的错误,一般与算法有关。
关于语法错误,不必多说,写代码时心细一点就可以解决。C++编译器的报错机制可以让我们轻松地解决这些错误。
第二种是运行时的错误,常见的有文件打开失败、数组下标溢出、系统内存不足等等。而一旦出现这些问题,引发算法失效、程序运行时无故停止等故障也是常有的。这就要求我们在设计软件算法时要全面。比如针对文件打开失败的情况,保护的方法有很多种,最简单的就是使用“return”命令,告诉上层调用者函数执行失败;另外一种处理策略就是利用c++的异常机制,抛出异常。
二、c++异常处理机制
C++异常处理机制是一个用来有效地处理运行错误的非常强大且灵活的工具,它提供了更多的弹性、安全性和稳固性,克服了传统方法所带来的问题.
异常的抛出和处理主要使用了以下三个关键字: try、 throw 、 catch 。
抛出异常即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常。该语句的格式为:
throw 表达式;
如果在try语句块的程序段中(包括在其中调用的函数)发现了异常,且抛弃了该异常,则这个异常就可以被try语句块后的某个catch语句所捕获并处理,捕获和处理的条件是被抛弃的异常的类型与catch语句的异常类型相匹配。由于C++使用数据类型来区分不同的异常,因此在判断异常时,throw语句中的表达式的值就没有实际意义,而表达式的类型就特别重要。
try-catch语句形式如下 :

  1. try
  2. {
  3.         包含可能抛出异常的语句;
  4. }
  5. catch(类型名 [形参名]) // 捕获特定类型的异常
  6. {
  7. }
  8. catch(类型名 [形参名]) // 捕获特定类型的异常
  9. {
  10. }
  11. catch(…)    // 三个点则表示捕获所有类型的异常
  12. {
  13. }

【范例1】处理除数为0的异常。该范例将上述除数为0的异常可以用try/catch语句来捕获异常,并使用throw语句来抛出异常,从而实现异常处理,实现代码如代码清单1-1所示。
// 代码清单1-1

  1. #include<iostream.h>     //包含头文件
  2. #include<stdlib.h>
  3. double fuc(double x, double y) //定义函数
  4. {
  5.     if(y==0)
  6.     {
  7.         throw y;     //除数为0,抛出异常
  8.     }
  9.     return x/y;     //否则返回两个数的商
  10. }
  11. void main()
  12. {
  13.     double res;
  14.     try  //定义异常
  15.     {
  16.         res=fuc(2,3);
  17.         cout<<“The result of x/y is : “<<res<<endl;
  18.         res=fuc(4,0); 出现异常,函数内部会抛出异常
  19.     }
  20.     catch(double)             //捕获并处理异常
  21.     {
  22.          cerr<<“error of dividing zero.n”;
  23.          exit(1);                //异常退出程序
  24.     }
  25. }
【范例2】自定义异常类型 (在本文开始的代码中已经给出示范)
三、异常的接口声明
为了加强程序的可读性,使函数的用户能够方便地知道所使用的函数会抛出哪些异常,可以在函数的声明中列出这个函数可能抛出的所有异常类型,例如:

void fun() throw( A,B,C,D);

这表明函数fun()可能并且只可能抛出类型(A,B,C,D)及其子类型的异常。
如果在函数的声明中没有包括异常的接口声明,则此函数可以抛出任何类型的异常,例如:

void fun();

一个不会抛出任何类型异常的函数可以进行如下形式的声明:

void fun() thow();

五、异常处理中需要注意的问题
1. 如果抛出的异常一直没有函数捕获(catch),则会一直上传到c++运行系统那里,导致整个程序的终止
2. 一般在异常抛出后资源可以正常被释放,但注意如果在类的构造函数中抛出异常,系统是不会调用它的析构函数的,处理方法是:如果在构造函数中要抛出异常,则在抛出前要记得删除申请的资源。
3. 异常处理仅仅通过类型而不是通过值来匹配的,所以catch块的参数可以没有参数名称,只需要参数类型。
4. 函数原型中的异常说明要与实现中的异常说明一致,否则容易引起异常冲突。
5. 应该在throw语句后写上异常对象时,throw先通过Copy构造函数构造一个新对象,再把该新对象传递给 catch.
那么当异常抛出后新对象如何释放?
异常处理机制保证:异常抛出的新对象并非创建在函数栈上,而是创建在专用的异常栈上,因此它才可以跨接多个函数而传递到上层,否则在栈清空的过程中就会被销毁。所有从try到throw语句之间构造起来的对象的析构函数将被自动调用。但如果一直上溯到main函数后还没有找到匹配的catch块,那么系统调用terminate()终止整个程序,这种情况下不能保证所有局部对象会被正确地销毁。
6. catch块的参数推荐采用地址传递而不是值传递,不仅可以提高效率,还可以利用对象的多态性。另外,派生类的异常扑获要放到父类异常扑获的前面,否则,派生类的异常无法被扑获。
7. 编写异常说明时,要确保派生类成员函数的异常说明和基类成员函数的异常说明一致,即派生类改写的虚函数的异常说明至少要和对应的基类虚函数的异常说明相同,甚至更加严格,更特殊。

Categories
不学无术

C++ 精确到纳秒计时

#include 
using namespace std;
inline unsigned __int64 GetCycleCount()
{
	__asm _emit 0x0f
	__asm _emit 0x31
}
int main()
{
	unsigned long t0, t1;
	t0 = GetCycleCount();
	t1 = GetCycleCount();
	cout << t1 - t0 << endl;
	return 0;
}

参考文献:http://yanlinjust79.blog.163.com/blog/static/16950848220107254332975/

在Intel Pentium以上级别的CPU中,有一个称为“时间戳(Time Stamp)”的部件,它以64位无符号整型数的格式,记录了自CPU上电以来所经过的时钟周期数。由于目前的CPU主频都非常高,因此这个部件可以达到纳秒级的计时精度。这个精确性是上述两种方法所无法比拟的。
在Pentium以上的CPU中,提供了一条机器指令RDTSC(Read Time Stamp Counter)来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数返回值的寄存器,所以我们可以把这条指令看成是一个普通的函数调用。
这个方法的优点是:
1.高精度。可以直接达到纳秒级的计时精度(在1GHz的CPU上每个时钟周期就是一纳秒),这是其他计时方法所难以企及的。
2.成本低。timeGetTime 函数需要链接多媒体库winmm.lib,QueryPerformance* 函数根据MSDN的说明,需要硬件的支持(虽然我还没有见过不支持的机器)和KERNEL库的支持,所以二者都只能在Windows平台下使用(关于DOS平台下的高精度计时问题,可以参考《图形程序开发人员指南》,里面有关于控制定时器8253的详细说明)。但RDTSC指令是一条CPU指令,凡是i386平台下Pentium以上的机器均支持,甚至没有平台的限制(我相信i386版本UNIX和Linux下这个方法同样适用,但没有条件试验),而且函数调用的开销是最小的。
3.具有和CPU主频直接对应的速率关系。一个计数相当于1/(CPU主频Hz数)秒,这样只要知道了CPU的主频,可以直接计算出时间。这和QueryPerformanceCount不同,后者需要通过QueryPerformanceFrequency获取当前计数器每秒的计数次数才能换算成时间。
这个方法的缺点是:
1.现有的C/C++编译器多数不直接支持使用RDTSC指令,需要用直接嵌入机器码的方式编程,比较麻烦。
2.数据抖动比较厉害。其实对任何计量手段而言,精度和稳定性永远是一对矛盾。如果用低精度的timeGetTime来计时,基本上每次计时的结果都是相同的;而RDTSC指令每次结果都不一样,经常有几百甚至上千的差距。这是这种方法高精度本身固有的矛盾。

Categories
不学无术

C++ Gossip: 隨機存取檔案

本文来自:http://openhome.cc/Gossip/CppGossip/RadomAccessFile.html

使用 get 指標或 put 指標,可以自由的移動至檔案中指定的位置進行讀取或寫入的動作,通常隨機存取檔案會使用二進位模式進行,文字模式開啟的檔案並不適合作隨機存取的動作。
如何利用隨機存取來讀寫所有的資料,必須視您的需求而定,需求決定您的資料結構,這邊以一個最簡單的例子來示範隨機存取,寫入檔案時都是使用固定大小的struct,由於資料大小固定,這可以方便明確的指定檔案中讀取的位置。
假設有一個簡單的學生成績資料如下:

  • Student.h
#ifndef DATASTRU_H
#define DATASTRU_H
struct Student {
    int studyNumber;
    char name[80];
    double score;
};
#endif

一個結構的大小是固定的,當要寫入一個結構時,可以使用這樣的語法:

fout.write(reinterpret_cast<const char*> (&student),
            sizeof(Student));


其中student是Student自訂struct所宣告的變數名稱,由於write接受const char*型態的變數,所以使用reinterpret_cast<const char*> 將之轉換為const char*指標。
下面這個程式示範,如何建立一個檔案,當中包括50筆空的資料:

  • main.cpp
#include <iostream>
#include <fstream>
#include "Student.h"
using namespace std;
int main(int argc, char* argv[]) {
    if(argc != 2) {
        cout << "指令: create <filename>" << endl;
        return 1;
    }
    ofstream fout(argv[1], ios::binary);
    if(!fout) {
        cerr << "檔案輸出失敗" << endl;
        return 1;
    }
    int count;
    cout << "要建立幾筆資料? ";
    cin >> count;
    Student student = {0, "", 0.0};
    for(int i = 0; i < count; i++) {
        fout.write(reinterpret_cast<const char*> (&student),
            sizeof(Student));
    }
    fout.close();
    return 0;
}

執行結果:

create data.bin
要建立幾筆資料? 50

接下來可以使用下面這個程式進行隨機存取,使用學號來作資料的位置指定,將之儲存在檔案中的指定位置:

#include <iostream>
#include <fstream>
#include "Student.h"
using namespace std;
int main(int argc, char* argv[]) {
    Student student;
    int count = 0;
    if(argc < 2) {
        cerr << "指令: write <filename>";
        return 1;
    }
    fstream fio(argv[1], ios::in | ios::out | ios::binary);
    if(!fio) {
        cerr << "無法讀取檔案" << endl;
        return 1;
    }
    while(true) {
        fio.read(reinterpret_cast<char *> (&student),
            sizeof(Student));
        if(!fio.eof())
            count++;
        else
            break;
    }
    fio.clear();
    cout << "輸入學號(1-" << count << ")"  << endl
         << "輸入0離開";
    while(true) {
        cout << "n學號? ";
        cin >> student.studyNumber;
        if(student.studyNumber == 0)
            break;
        cout << "輸入姓名, 分數" << endl
             << "? ";
        cin >> student.name >> student.score;
        fio.seekp((student.studyNumber - 1) * sizeof(Student));
        fio.write(reinterpret_cast<const char*> (&student),
            sizeof(Student));
    }
    fio.close();
    return 0;
}

執行結果:

write data.bin
輸入學號(1-50)
輸入0離開
學號? 1
輸入姓名, 分數
? 良葛格 88
學號? 2
輸入姓名, 分數
? 毛妹妹 94
學號? 5
輸入姓名, 分數
? 毛毛蟲 75
學號? 0

接下來可以使用下面這個程式讀取方才所輸入的資料:

#include <iostream>
#include <fstream>
#include "Student.h"
using namespace std;
int main(int argc, char* argv[]) {
    Student student;
    int count = 0, number;
    if(argc != 2) {
        cout << "指令: read <filename>" << endl;
        return 1;
    }
    ifstream fin(argv[1], ios::in | ios::binary);
    if(!fin) {
        cerr << "無法讀取檔案" << endl;
        return 1;
    }
    while(true) {
        fin.read(reinterpret_cast<char *> (&student),
            sizeof(Student));
        if(!fin.eof())
            count++;
        else
            break;
    }
    fin.clear();
    cout << "輸入學號(1-" << count << ")"  << endl
         << "輸入0離開";
    while(true) {
        cout << "n學號? ";
        cin >> number;
        if(number == 0)
            break;
        else if(number > count) {
            cout << "輸入學號(1-" << count << ")" << endl;
            continue;
        }
        cout << "n學號t姓名tt分數" << endl;
        fin.seekg((number - 1) * sizeof(Student));
        fin.read(reinterpret_cast<char*> (&student),
            sizeof(Student));
        cout << student.studyNumber << "t"
             << student.name << "tt"
             << student.score << endl;
    }
    fin.close();
    return 0;
}

執行結果:

read data.bin
輸入學號(1-50)
輸入0離開
學號? 1
學號    姓名            分數
1       良葛格          88
學號? 2
學號    姓名            分數
2       毛妹妹          94
學號? 3
學號    姓名            分數
0                       0
學號? 5
學號    姓名            分數
5       毛毛蟲          75
學號? 0

這幾個程式是隨機存取的簡單示範,您也可以結合起來,製作一個簡易的成績登記程式。
在判斷資料筆數時還有更簡單的方法,就是開啟檔案後先使用ios::end將指標移至檔案尾,然後使用tellg()得到目前的檔案指標位置,再除以資料結構的大小除可得知資料筆數,例如:

file.seekg(0, ios::end);
count = file.tellg() / sizeof(資料結構);
 


 

关闭提示 关闭

确 认 取 消