程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++編程調試秘笈----一些雜項

C++編程調試秘笈----一些雜項

編輯:C++入門知識

1、避免編寫拷貝構造函數和賦值操作符,如果默認版本並不適用,可以考慮把拷貝構造函數和賦值操作符聲明為私有,禁止類實例的復制 2、避免在析構函數中編寫代碼 需要析構函數的原因可能有好幾個: a、在基類中,可能需要聲明虛擬析構函數,這樣就可以使用一個指向基類的指針指向一個派生類的實例 b、在派生類中,並不需要把析構函數聲明為虛擬函數,但是為了增加可讀性,也可以這樣做 c、可能需要聲明析構函數並不拋出任何異常 下來討論一下為什麼析構函數應該是空的: [cpp]   class Student   {   public:       Student       {           if (!number_)           {               number_ = new int(age);           }       }          ~Student()       {           if (number_)           {               delete number_;           }       }      private:       int* number_;   };     看看這個Student類,因為他在構造函數中獲取了一些資源,所以需要在析構函數中釋放掉。 但是現在問題來了,這個類的設計----每次添加一個表示個人描述的新元素(例如number_)時,都需要在析構函數中添加對應的清理代碼,這就違背了“不要迫使程序員記住某些事情”的原則;另一個問題是,缺少安全檢查,首先你的number_可能是要必須大於0的,這樣還得在new的前面進行if判斷,另外一個極端的情況: [cpp]  #include "stdafx.h"   #include "iostream"   #include "string"      class A   {   public:       A() { std::cout << "Creating A" << std::endl; }       ~A(){ std::cout << "Destroying A" << std::endl; }   };      class B   {   public:       B() { std::cout << "Creating B" << std::endl; }       ~B(){ std::cout << "Destroying B" << std::endl; }   };      class C : public A   {   public:       C()        {            std::cout << "Creating C" << std::endl;            throw "Don't like C";       }       ~C(){ std::cout << "Destroying C" << std::endl; }   private:       B b_;   };      int _tmain(int argc, _TCHAR* argv[])   {       try       {           C c;       }       catch (...)       {           std::cout << "Caught an exception" << std::endl;       }          return 0;   }     因為在C中throw了一個異常,所以導致class C的析構函數沒有被執行,這就導致了一些問題的產生 改成下面的形式設計就要好得多: [cpp]  class Student   {   public:       Student(const int number, const char* name)       {           SCPP_ASSERT(number > 0, "number must be large 0");           number_ = new int(number);           SCPP_ASSERT(name != NULL, "name must be not null");           name_ = new std::string(name);       }          ~Student()       {       }   private:       Student(const Student& student);       Student& operator = (const Student&);          scpp::ScopedPtr<int> number_;       scpp::ScopedPtr<std::string> name_;   };     即使第二個安全檢查拋出異常,執行name_的智能指針的析構函數仍然會被調用,並執行他的清理工作。另一個附帶的好處是,我們並不需要操心把這些智能指針初始化為NULL,這是自動完成的。因此,可以看到構造函數拋出異常是一種潛在的危險行為;對應的析構函數將不會被調用,因此可能會存在問題,除非析構函數是空函數。 3、編寫一致的比較操作符 常用的操作符有< > <= >= == !=,站在C++的角度,這些操作可以寫成6個相互完全獨立的函數,但是他們相互之間又有聯系,比如x1>x2成立,x2<x1和x1>=x2也是成立的。所以,我們需要一些步驟來幫我實現這個目標(往之前的scpp_types.h上面添加): [cpp]   #ifndef __SCCP_TYPES_H__   #define __SCCP_TYPES_H__      #include <ostream>   #include "scpp_assert.h"      template <typename T>   class TNumber   {   public:       TNumber(const T& x =0 )           :data_(x)       {          }       operator T() const        {           return data_;       }          TNumber &operator = (const T& x)       {           data_ = x;           return *this;       }          TNumber operator ++(int)       {           TNumber<T> copy(*this);           ++data_;           return copy;       }          TNumber operator ++()       {           ++data_;           return *this;       }          TNumber& operator += (T x)       {           data_ += x;           return *this;       }          TNumber& operator -= (T x)       {           data_ -= x;           return *this;       }          TNumber& operator *= (T x)       {           data_ *= x;           return *this;       }          TNumber& operator /= (T x)       {           SCPP_ASSERT(x != 0, "Attempt to divide by 0");           data_ /= x;           return *this;       }          T operator / (T x)       {           SCPP_ASSERT(x != 0, "Attempt to divide by 0");           return data_ / x;       }      private:       T data_;   };      typedef long long int64;   typedef unsigned long long unsigned64;      typedef TNumber<int>      Int;   typedef TNumber<unsigned> Unsigned;   typedef TNumber<int64>        Int64;   typedef TNumber<unsigned64> Unsigned64;   typedef TNumber<float>        Float;   typedef TNumber<double>       Double;   typedef TNumber<char>     Char;      class Bool   {   public:       Bool(bool x = false)           :data_(x)       {       }          operator bool () const       {           return data_;       }          Bool& operator = (bool x)       {           data_ = x;           return *this;       }          Bool& operator &= (bool x)       {           data_ &= x;           return *this;       }          Bool& operator |= (bool x)       {           data_ |= x;           return *this;       }      private:       bool data_;   };      inline std::ostream& operator << (std::ostream& os, Bool b)   {       if (b)       {           os << "True";       }       else       {           os << "False";       }       return os;   }      #define SCPP_DEFINE_COMPARISON_OPERATORS(Class) \       bool operator < (const Class& that) const { return CompareTo(that) < 0; } \       bool operator > (const Class& that) const { return CompareTo(that) > 0; } \       bool operator ==(const Class& that) const { return CompareTo(that) ==0; } \       bool operator <=(const Class& that) const { return CompareTo(that) <=0; } \       bool operator >=(const Class& that) const { return CompareTo(that) >=0; } \       bool operator !=(const Class& that) const { return CompareTo(that) !=0; }       #endif       測試代碼(vs2012+win7環境): [cpp]   #include "stdafx.h"   #include "scpp_assert.h"   #include "iostream"   #include "scpp_vector.h"   #include "scpp_array.h"   #include "scpp_matrix.h"   #include "algorithm"   #include "scpp_types.h"   #include "scpp_refcountptr.h"   #include "scpp_scopedptr.h"   #include "scpp_ptr.h"   #include "string"      #define STUDENTAGE 10      class Student   {   public:       Student(int age)       {           SCPP_ASSERT(age > 0, "number must be large 0");           age_ = age;       }          int CompareTo(const Student& that) const       {           if (this->age_ > that.age_)           {               return 1;           }           else if(this->age_ == that.age_)           {               return 0;           }           else           {               return -1;           }       }       SCPP_DEFINE_COMPARISON_OPERATORS(Student);   private:       int age_;   };      int _tmain(int argc, _TCHAR* argv[])   {       Student studentFirst(STUDENTAGE);       Student studentSecond(STUDENTAGE);          if (studentFirst >= studentSecond)       {           std::cout << "(studentFirst > studentSecond) && (studentFirst == studentSecond)" << std::endl;       }          return 0;   }       4、使用標准C函數庫的錯誤 C函數庫中,在好幾個方面都是不安全的,還可能導致程序崩潰 比如使用stdio.h中想sprintf()這樣的函數可能會有下面的問題: a、有些函數接受字符數組的指針(char *),如果向他們傳遞一個NULL而不是指向合法的C字符串的指針,將會崩潰(比如strlen(NULL);就可以秒掉程序) b、有些函數將寫入一個緩沖區,他們可能會改寫越過緩沖區尾部的內存,從而導致不可預料的程序行為 ..... 上面的問題,可以這樣處理: a、提供執行所有必要安全檢查的版本,並按照與處理空字符串的相同方式處理NULL指針 b、對於不能犧牲字符串操作速度的應用程序,提供只在測試時才激活的臨時安全檢查的函數版本 ...... 但是,這些問題最好的解決方案是不用C字符串函數庫,而是改用C++所提供的類,比如: strlen(name)可以改用name.size(),對於strcpy()可以僅僅 使用字符串賦值操作符;strcat()或strncat()可以改用: [cpp]  int _tmain(int argc, _TCHAR* argv[])   {          std::ostringstream buffer;       buffer << "zeng";       buffer << "raoli";       std::string result = buffer.str();          std::cout << "this result string is : " << result << std::endl;          return 0;   }     或者采用更簡短的形式: [cpp]   int _tmain(int argc, _TCHAR* argv[])   {       std::string result = "zeng";       result += "raoli";          std::cout << "this result string is : " << result << std::endl;          return 0;   }     這些代碼不僅更容易閱讀,並且更安全,而且對於較長的字符串,他們的速度也快於strcat()!因為不存在需要分配和改寫緩沖區

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved