メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

計算量の多い値返し

関数が参照やポインターではなく値で大きな出力を返す

説明

この欠陥は、関数が、大きな出力オブジェクトを参照やポインターで返すのではなく、値で返す場合に発生します。

このチェッカーは、以下の条件の両方が true の場合に起動されます。

  • 返されたオブジェクトのアドレスが return ステートメント後も有効なままである。

  • 返されたオブジェクトが以下のいずれかである。

    • 非トリビアル コピー可能なオブジェクト。このようなオブジェクトを値で返すには、参照で返すよりも計算量の多い外部関数呼び出しが必要になります。オブジェクトが非トリビアル コピー可能かどうかをチェックするには、関数 std::is_trivially_copyable を使用します。この関数の詳細については、C++ リファレンスの std::is_trivially_copyable を参照してください。

    • 大きなトリビアル コピー可能なオブジェクト。オブジェクトが大きい場合は、トリビアル コピー可能なオブジェクトを値で返すと計算量が多くなります。

この欠陥は、返されたオブジェクトが以下の場合は発生しません。

  • コピーの計算量が少ない。

  • 一時オブジェクトまたは非静的ローカル オブジェクト。

リスク

オブジェクトを参照またはポインターで返すことができる場合は、大きなオブジェクトを値で返そうとすると非効率になります。関数は、& または * が欠落していることによって、大きなオブジェクトを誤って値で返す場合があります。このような非効率な return ステートメントは検出されない可能性があります。次のコードについて考えます。

#include<string>
class Buffer{
public:
	//..
	const std::string getName() {
		return m_names;
	}
	//...
private:
	std::string m_names;
};
クラス Buffer には、大きなプライベート オブジェクト m_names が含まれています。このようなプライベート オブジェクトには、getName などの大きなオブジェクト m_names を返す公開 getter 関数を使用するのが一般的です。getNames の戻り値の型は const std::string& ではなく const std::string として設定されるため、関数は、参照ではなく、値で大きなオブジェクトを返します。& が欠落しているにもかかわらず、このコードはコンパイル可能で、正しく機能するため、計算量の多いコピーによる戻しが検出されない可能性があります。同様の非効率性の原因については、計算量の多い値渡し範囲ベースの for ループの反復における計算量の多いコピーを参照してください。

修正方法

この欠陥を修正するには、参照を使用することによってオブジェクトを返します。C コードを使用している場合は、値でオブジェクトを返さないためにポインターを使用します。

  • C++ 関数から参照によってオブジェクトを返すには、関数の戻り値の型を参照として設定します。次に例を示します。

    #include<string>
    class Buffer{
    public:
    	//..
    	const std::string& getName() {
    		return m_name;
    	}
    	//...
    private:
    	std::string m_name;
    };
    関数 getName() は、大きなオブジェクト m_names を参照で返します。これは、この関数の戻り値の型が参照である const std::string& のためです。

  • または、値でオブジェクトを返さないためにポインターを使用します。たとえば、getName() の戻り値の型を const std::string* に設定してから、m_names のアドレスを &m_names として返します。

    #include<string>
    class Buffer{
    public:
    	//..
    	const std::string* getName() {
    		return &m_name;
    	}
    	//...
    private:
    	std::string m_name;
    };
    ポインターを使用することによって、関数 getName()m_names を値で返さなくなります。この方法は、参照が使用できない C コードで有効です。

    パフォーマンスの改善の程度は、使用しているコンパイラ、ライブラリ実装、環境によって異なる可能性があります。

すべて展開する

#include<string>
#include<memory>
#include<array>
#include<cstdint>
class Buffer{	
private:
	static const size_t SIZE = 10;
	std::string m_name; //Nontrivially copyable type
	std::array<uint8_t,SIZE> m_byteArray; // Large trivially copyable type 
	size_t m_currentSize; // Small trivially copyable
public:
	//...
	const std::string getName(){ 
		return m_name;
	}
	const std::array<uint8_t,SIZE> getByteArray(){ 
		return m_byteArray;
	}
	size_t getCurrentSize(){ 
		return m_currentSize;
	}
};

この例では、クラス Buffer 内のさまざまなプライベート オブジェクトが getter 関数によってアクセスされます。

  • 大きなオブジェクト m_name は、getter 関数 getName によって値で返されます。この非トリビアル コピー可能なオブジェクトを参照で返すことができる場合に値で返すと、非効率になります。Polyspace® は、この関数にフラグを設定します。

  • オブジェクト m_byteArray は、getter 関数 getByteArray によって値で返されます。この大きなオブジェクトを参照で返すことができる場合に値で返すと、非効率になります。Polyspace は、この関数にフラグを設定します。

  • 関数 getCurrentSize は、整数 m_currentSize を値で返します。この小さなオブジェクトのコピーは非効率になりません。Polyspace は、この関数にフラグを設定しません。

修正

これらの欠陥を修正するには、getter 関数の戻り値の型として参照を使用することによって大きなオブジェクトを返します。たとえば、getName の戻り値の型を const std::string ではなく const std::string& に設定します。

#include<string>
#include<memory>
#include<array>
#include<cstdint>
class Buffer{	
private:
	static const size_t SIZE = 10;
	std::string m_name; //Nontrivially copyable type
	std::array<uint8_t,SIZE> m_byteArray; // Large trivially copyable type 
	size_t m_currentSize; // Small trivially copyable
public:
	//...
	const std::string& getName(){ 
		return m_name;	
	}
	const std::array<uint8_t,SIZE>& getByteArray(){ 
		return m_byteArray;
	}
	size_t getCurrentSize(){ 
		return m_currentSize;
	}
};
typedef struct _Circle{
	double Origin_abscissa;
	double Origin_ordinate;
	double Radius;
	char name[10];
}Circle;

const Circle getCircle(){
	static Circle SpecificCircle;
	//...
	return SpecificCircle;
}

この例では、関数 getCircle が大きな static オブジェクト SpecificCircle を値で返します。Polyspace は、この関数にフラグを設定します。

修正

この欠陥を修正するには、ポインターを使用することによってオブジェクト SpecificCircle を返します。関数 getCircle の戻り値の型を const Circle ではなく const Circle* として宣言してから、オブジェクトのアドレス、つまり、&SpecificCircle を返します。

typedef struct _Circle{
	double O_abscissa;
	double O_ordinate;
	double Radius;
	char name[10];
}Circle;

const Circle* getCircle(){
	static Circle SpecificCircle;
	//...
	return &SpecificCircle;
}

結果情報

グループ: パフォーマンス
言語: C | C++
既定値: オフ
コマンド ライン構文: EXPENSIVE_RETURN_BY_VALUE
影響度: Medium

バージョン履歴

R2020b で導入