0

//ヘッダー ファイル内: クラス定義:

class myString
{
public:

        myString(void);
        myString(const char *str);

        myString(const myString &); //copy constructor 
        ~myString(void); //destructor

        void swap(myString &from);


private:

        char *stringPtr;
        int stringLen;
};

// cpp ファイルで、メンバー関数を定義します

myString::myString(const char *str)
{
    stringLen = strlen(str);

    stringPtr = new char[stringLen+1];

    strcpy(stringPtr,str);
    cout << "constructor with parameter called"<<endl;
}

myString::myString(const myString &str)
{

    stringPtr = new char[str.stringLen +1];
    strcpy(stringPtr,str.stringPtr);
    cout << "copyconstructor"<<endl;
}


void myString::swap(myString &from)
{
    myString buffer(from);
    int lengthBuffer = from.stringLen;

    from = new char[stringLen+1];
    from.stringLen = stringLen;
    strcpy(from.stringPtr, stringPtr);


    stringPtr = new char[lengthBuffer+1];
    stringLen = lengthBuffer;
    strcpy(stringPtr,buffer.stringPtr);
}
4

4 に答える 4

2

参照を変更することはできません。ポインターを変更するポインターに置き換えても、指しているオブジェクトは変更されません。代わりに、参照を処理する必要があります。フィールドを交換するだけです。

void myString::swap(myString &from)
{
    std::swap( stringLen, from.stringLen );
    std::swap( stringPtr, from.stringPtr );
}

上記は、コメントでユーザー sbiが提案した std::swap() を使用しています。これは、次のものと完全に同等です (説明のためだけに、STL を再発明しないでください)。

void myString::swap(myString &from)
    // First remember own length and pointer
    const int myOldLen = stringLen;
    char* myOldPtr = stringPtr;
    // now copy the length and pointer from that other string
    stringLen = from.stringLen;
    stringPtr = from.stringPtr;
    // copy remembered length and pointer to that other string
    from.StringLen = myOldLen;
    from.StringPtr = myOldPtr;
    // done swapping
}

どちらも、セルフスワッピングで呼び出された場合でも機能します。

myString string;
string.swap( string );
于 2009-10-22T06:17:26.550 に答える
1

関数のエラーに関して、すでにいくつかの良い回答を得ていますmyString::swap()。それでも、もう1つ追加したいと思います。その関数には多くの問題があり、最初はどこから始めればよいかを考えるのが難しいことがわかりました。しかし、私が指摘したいいくつかの基本的な問題であなたが失敗していることに気付きました:

慣例として、呼び出された関数swapはそのタスクを実行することが期待されています

  1. O(1)で
  2. 例外をスローすることなく。

(はい、私は知っています。例外があります: std::tr1::array<>::swap()。しかし、それらは非常に正当化されるべきです。) あなたの実装は両方のアカウントで失敗します。これは O(n) ( strcpy) であり、例外 ( ) をスローする可能性がありnewます。これは不必要に正当な理由もなく行われます。

を見るとmyString、メンバー データが 2 つしかなく、どちらも組み込み型であることがわかります。つまり、このクラスの 2 つのオブジェクトを交換することは、上記の規則を守りながら行うのは非常に簡単です: メンバー データを交換するだけです。それはそれらを呼び出すのと同じくらい簡単std::swapです:

void myString::swap(myString &from)
{
  std::swap(this->stringPtr,from.stringPtr);
  std::swap(this->stringLen,from.stringLen);
}

これは決して失敗しません (2 つのポインターと 2 つの整数の交換は失敗しません)、O(1) で実行され、非常に理解しやすいです (とにかく、その交換を理解すれば、それは実装の慣用的な形式です)クラス固有のswap関数)、標準ライブラリで十分にテストされたものを呼び出す 2 行のコードで構成され、エラーが発生しやすい (そして、あなたの場合は誤った) 手動メモリ管理を行う 8 行のコードではありません。

注 1: これが完了したらstd::swap、クラスの実装を呼び出すように特化する必要があります。

namespace std { // only allowed for specializing function templates in the std lib
  template<>
  inline void std::swap<myString>(myString& lhs, myString& rhs)
  {
    lhs.swap(rhs);
  }

注 2: クラスの代入を実装する最良の (単純で、例外セーフで、自己代入セーフ) 方法は、その を使用することswapです。

myString& myString::operator=(const myString& rhs)
{
   myString tmp(rhs); // invoke copy ctor
   this->swap(tmp); // steal data from temp and leave it with our own old data
   return *this;
} // tmp will automatically be destroyed and takes our old data with it
于 2009-10-22T07:32:01.907 に答える
0

from = new char[stringLen+1];する必要がありますfrom.stringPtr = new char[stringLen+1];。また、新しいメモリを割り当てる前に、以前に割り当てられたメモリを解放することを忘れないでください。

于 2009-10-22T06:21:18.520 に答える